Tuesday, December 9, 2014

MPL3115A2 sensor with Raspberry Pi

Here is a short write up on using the MPL3115A2 with the Raspberry Pi.

The MPL3115A2 is a precision pressure and temperature sensor. I bought mine from SparkFun.
MPL3115A2:  https://www.sparkfun.com/products/11084

Installing I2C for Pi

Getting started you'll have to install the I2C packages for Raspberry Pi, and enable them.
From a Raspberry Pi terminal use the following commands:
sudo apt-get install python-smbus
sudo apt-get install i2c-tools

Enable the modules by adding them to /etc/modules
sudo nano /etc/modules
and add the following two lines:
i2c-bcm2708
i2c-dev

Remove the modules from the blacklist by commenting them out "add # to the front"
sudo nano /etc/modprobe.d/raspi-blacklist.conf
Make sure the spi and i2c lines are commented out:
#blacklist spi-bcm2708
#blacklist i2c-bcm2708

Lastly, the MPL3115A2 requires a proper repeated start command in it's I2C communication. Raspberry Pi doesn't do this out of the box, but there is a kernel module that can be enabled to make it perform repeated start correctly. Run the following commands to enable repeated start on the Pi:
sudo su -
echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined
exit
More details about the repeated start problem can be found here:
http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840&start=25

Connecting the Sensor

Next solder some headers to the breakout and connect it to the Raspberry Pi. You'll want to use the following Raspberry Pi GPIO pinout:
pinsensor
1VCC
3SDA
5SCL
6Ground


Python I2C Code

Finally, we need to write some code to read data from the sensor. The following is an example Python program to use smbus to read data from the sensor.

MPL3115A2.py
from smbus import SMBus
import time

# Special Chars
deg = u'\N{DEGREE SIGN}'

# I2C Constants
ADDR = 0x60
CTRL_REG1 = 0x26
PT_DATA_CFG = 0x13
bus = SMBus(1)

who_am_i = bus.read_byte_data(ADDR, 0x0C)
print hex(who_am_i)
if who_am_i != 0xc4:
    print "Device not active."
    exit(1)

# Set oversample rate to 128
setting = bus.read_byte_data(ADDR, CTRL_REG1)
newSetting = setting | 0x38
bus.write_byte_data(ADDR, CTRL_REG1, newSetting)

# Enable event flags
bus.write_byte_data(ADDR, PT_DATA_CFG, 0x07)

# Toggel One Shot
setting = bus.read_byte_data(ADDR, CTRL_REG1)
if (setting & 0x02) == 0:
    bus.write_byte_data(ADDR, CTRL_REG1, (setting | 0x02))

# Read sensor data
print "Waiting for data..."
status = bus.read_byte_data(ADDR,0x00)
while (status & 0x08) == 0:
    #print bin(status)
    status = bus.read_byte_data(ADDR,0x00)
    time.sleep(0.5)

print "Reading sensor data..."
p_data = bus.read_i2c_block_data(ADDR,0x01,3)
t_data = bus.read_i2c_block_data(ADDR,0x04,2)
status = bus.read_byte_data(ADDR,0x00)
print "status: "+bin(status)

p_msb = p_data[0]
p_csb = p_data[1]
p_lsb = p_data[2]
t_msb = t_data[0]
t_lsb = t_data[1]

pressure = (p_msb << 10) | (p_csb << 2) | (p_lsb >> 6)
p_decimal = ((p_lsb & 0x30) >> 4)/4.0

celsius = t_msb + (t_lsb >> 4)/16.0
fahrenheit = (celsius * 9)/5 + 32

print "Pressure and Temperature at "+time.strftime('%m/%d/%Y %H:%M:%S%z')
print str(pressure+p_decimal)+" Pa"
print str(celsius)+deg+"C"
print str(fahrenheit)+deg+"F"

Sample Output:
pi@raspberrypi ~ $ sudo python MPL3115A2.py
0xc4
Waiting for data...
Reading sensor data...
status: 0b0
Pressure and Temperature at 12/10/2014 04:51:56+0000
99838.75 Pa
20.375°C
68.675°F

More Resources

MPL3115A2 Datasheet:
http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Pressure/MPL3115A2.pdf

Arduino code for reference:
https://github.com/sparkfun/MPL3115A2_Breakout/blob/master/firmware/MPL3115A2/MPL3115A2.ino

17 comments:

  1. Did you change any of the settings previously? When I copy and run your code 1:1 it works but shows odd values for the pressure. Instead of a value around 100,000 I get values around 130.
    So I wonder if there are some registers that you need to set before running this code for the first time? Since you have these two lines you are using the previous settings:

    setting = bus.read_byte_data(ADDR, CTRL_REG1)
    newSetting = setting | 0x38

    The temperature values that my sensor returns are correct though so clearly it has to do with some settings for the barometer. Looking through the data sheet I don't quite see though what that could be.

    ReplyDelete
    Replies
    1. I should add that the p_MSB byte always comes out as zero

      Delete
  2. Found the problem. You need to change the line
    newSetting = setting | 0x38
    to
    newSetting = 0x38
    This will guarantee that it works as a barometer. For some reason it was set to work as an altimeter by default. Although the data sheet claims that barometer is the default but maybe the factory quality control test when they assembled the module changed it to altimeter.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I keep getting some variation of the following:

    0xc4
    Traceback (most recent call last):
    File "mpl3115a2.py", line 20, in
    setting = bus.read_byte_data(ADDR, CTRL_REG1)
    IOError: [Errno 110] Connection timed out

    Sometimes it doesn't even make it past who_am_i query.

    I'm getting similar results with Node.js and am wondering if maybe someone has some advice?

    ReplyDelete
    Replies
    1. Hey John,

      I'm having the issue that you have, it never past who_am_i query. The display is always 0x0, Device not active.

      Have you found any solutions to solve this problem yet?

      Thanks in advanced.

      Delete
  5. Fixed it. Had an erroneous i2c_arm_baudrate set in /boot/config.txt

    ReplyDelete
  6. What exact setting did you use?

    ReplyDelete
  7. I had what I assume was a terminal connection issue (or maybe a vi macro mistake) that rendered a series of ones and zeros probably a dozen digits long. It wasn't even remotely a correct setting. Since I discovered that it has been working perfectly. I wasn't able to get the second i2c channel to work with another MPL3115A2 but luckily I had several multiplexers lying about. I'm currently using the two sensors to approximate air flow through an aperture and things seem to be working well. Thank you for this incredibly helpful writeup.

    ReplyDelete
  8. I had what I assume was a terminal connection issue (or maybe a vi macro mistake) that rendered a series of ones and zeros probably a dozen digits long. It wasn't even remotely a correct setting. Since I discovered that it has been working perfectly. I wasn't able to get the second i2c channel to work with another MPL3115A2 but luckily I had several multiplexers lying about. I'm currently using the two sensors to approximate air flow through an aperture and things seem to be working well. Thank you for this incredibly helpful writeup.

    ReplyDelete
  9. I got a MPL3115A2 from adafruit. I am having a problem where your script says my device is not active. Just wonder how to get pass this part. I think I have a active device and i2cdetect shows something working.

    pi@rpi ~ $ sudo python MPL3115A2.py
    0x0
    Device not active.

    pi@rpi ~ $ sudo i2cdetect -y 1
    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --

    pi@rpi ~ $ cat /sys/module/i2c_bcm2708/parameters/combined
    Y

    Thanks for your help.

    Doug

    ReplyDelete
    Replies
    1. did this problem ever get fixed for you? I'm having the same problem

      Delete
  10. I discarded that bit of code when I translated from Python to javascript for Node. If I recall, I had issues with it too. I could still use the sensor without looking for the WHO_AM_I every time. Maybe the author has a solution.

    ReplyDelete
  11. Can you please answer these questions?
    1.
    Where did you find out the MPL3115A2 requires a proper repeated start command in it's I2C communication? I browsed a datasheet, but did not find. I probably overlooked it or used a different manual.

    2. After
    sudo su -
    echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined
    exit
    . How can I check my Raspberry was setup properly?

    3.
    what param is the value '3' in command
    p_data = bus.read_i2c_block_data(ADDR,0x01,3)



    ReplyDelete
  12. For those ones interested in using MPL3115A2 + python on Raspberry pi.
    There is one good guy who wrote a hole bunch of stuff for RPI including python code for MPL3115A2. Here is a link:

    https://github.com/sensorian/sensorian-firmware/tree/master/Drivers_Python/MPL3115A2

    ReplyDelete
  13. After rebooting the Pi the 'combined' parameter is back to being turned off.

    To make the change permanent, you might want to create a file in /etc/modprobe.d containint the following line:
    options i2c_bcm2708 combined=1

    That will survive a reboot.

    ReplyDelete
  14. I used this http://www.instructables.com/id/Raspberry-Pi-MPL3115A2-Precision-Altimeter-Sensor--1/?ALLSTEPS works with Pi3 (enable I2C first, install Python SMBus libraries)

    ReplyDelete