Read barometric pressure and temperature with Java from Raspberry Pi 3 & BMP180

Introduction

Use the Raspberry Pi 3 to read barometric pressure and temperature information from the barometric pressure / temperature sensor BMP180. If you specify the sea level pressure, the altitude of the measurement point will be calculated based on the measured pressure. The programming language uses Java.

BMP180

Preparation

Use Pi4J to control the GPIO of the Raspberry Pi from Java. For the Pi4J environment, refer to "Installing Pi4J" in the following article.

Graph the sensor information of Raspberry Pi and prepare an environment that can be checked with a Web browser

Also, since this sensor communicates via I2C, enable the I2C communication function of the Raspberry Pi (it is disabled by default).

To enable it from the Raspbian GUI, open the settings panel from "Settings"-"Raspberry Pi Settings" from the "Menu" button on the taskbar, and select the "Interface" tab to enable "I2C". To enable it from the command line, enter the sudo raspi-config command, select Interfacing Options> I2C, and finally select Yes.

After setting, reboot just in case.

The Raspberry Pi and BMP180 are connected as follows.

BMP180_ブレッドボード.png

Sensor operating mode

When reading the barometric pressure, this sensor specifies one of four modes to read. The number of samplings (accuracy) and power consumption differ depending on the specified mode. This mode is valid only when reading barometric pressure and is not relevant for reading temperature. BMP180_Datasheet.png

(Reference) BMP180 datasheet

program

Constitution

The program consists of the following two files. BMP180Demo.java BMP180.java

The sensor address assumes the default 77H. After connecting the sensor, enter the following command, and if it is recognized in a place other than 77H, set the value to the constant I2C_ADRESS defined in BMP180.java.

pi@raspberrypi:~ $ 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: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
pi@raspberrypi:~ $

Source code

BMP180Demo.java


import java.util.Date;
import java.util.concurrent.TimeUnit;

public class BMP180Demo {
    public static void main(String[] args) throws Exception {
        BMP180 bmp180 = new BMP180();                       // use I2C bus 1, standard mode
        // BMP180 bmp180 = new BMP180(BMP180.ULTRAHIGHRES);   // use I2C bus 1, ultra high resolution mode
        // BMP180 bmp180 = new BMP180(BMP180.HIGHRES);        // use I2C bus 1, high resolution mode
        // BMP180 bmp180 = new BMP180(BMP180.STANDARD);       // use I2C bus 1, standared mode
        // BMP180 bmp180 = new BMP180(BMP180.ULTRALOWPOWER);  // use I2C bus 1, ultra low power mode

        while (true) {
            System.out.println("Last valid input: " + new Date());

            double temperature = bmp180.readTemperature();
            System.out.printf("Temperature: %.2f C (%.1f F)\n",
                    temperature, BMP180.convertToFahrenheit(temperature));

            double pressure = bmp180.readPressure();
            System.out.printf("Pressure   : %.2f hPa\n", pressure);

            // bmp180.setStandardSeaLevelPressure(pressure); // specify sea level pressure in hPa
            System.out.printf("Altitude   : %.2f m\n\n", bmp180.readAltitude());

            TimeUnit.SECONDS.sleep(1);
        }
    }
}

BMP180.java


import java.io.IOException;
import java.util.concurrent.TimeUnit;

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.io.i2c.I2CFactory.UnsupportedBusNumberException;

public class BMP180 {
    // Hardware pressure sampling accuracy modes
    public static final int ULTRALOWPOWER = 0;
    public static final int STANDARD      = 1;
    public static final int HIGHRES       = 2;
    public static final int ULTRAHIGHRES  = 3;

    private int mode;

    // Registers
    private static final int CAL_AC1   = 0xAA;
    private static final int CTRL_MEAS = 0xF4;
    private static final int OUT_MSB   = 0xF6;

    // Commands
    private static final byte CMD_READTEMP     = 0x2E;
    private static final byte CMD_READPRESSURE = 0x34;

    private static final int I2C_BUS     = I2CBus.BUS_1;
    private static final int I2C_ADDRESS = 0x77;
    private I2CDevice device;

    private int AC1;
    private int AC2;
    private int AC3;
    private int AC4;
    private int AC5;
    private int AC6;
    private int B1 ;
    private int B2 ;
    private int MB ;
    private int MC ;
    private int MD ;

    private double standardSeaLevelPressure = 1013.89; // avarage sea level pressure in Tokyo

    public BMP180(int i2cBus, int i2cAddress, int mode) throws UnsupportedBusNumberException, IOException {
        // Create I2C bus
        I2CBus bus = I2CFactory.getInstance(i2cBus);

        // Get I2C device
        device = bus.getDevice(i2cAddress);

        // Calibration Coefficients stored in EEPROM of the device
        // Read 22 bytes of data from address 0xAA(170)
        byte[] data = new byte[22];
        device.read(CAL_AC1, data, 0, data.length);

        // Convert the data
        AC1 = INT (data[ 0], data[ 1]);
        AC2 = INT (data[ 2], data[ 3]);
        AC3 = INT (data[ 4], data[ 5]);
        AC4 = UINT(data[ 6], data[ 7]);
        AC5 = UINT(data[ 8], data[ 9]);
        AC6 = UINT(data[10], data[11]);
        B1  = INT (data[12], data[13]);
        B2  = INT (data[14], data[15]);
        MB  = INT (data[16], data[17]);
        MC  = INT (data[18], data[19]);
        MD  = INT (data[20], data[21]);

        this.mode = mode;
    }

    public BMP180(int mode) throws UnsupportedBusNumberException, IOException {
        this(I2C_BUS, I2C_ADDRESS, mode);
    }

    public BMP180() throws UnsupportedBusNumberException, IOException {
        this(BMP180.STANDARD);
    }

    private int readAndCalcB5() throws IOException, InterruptedException {
        // Select measurement control register
        // Enable temperature measurement
        device.write(CTRL_MEAS, CMD_READTEMP);
        TimeUnit.MILLISECONDS.sleep(5);

        // Read 2 bytes of data from address 0xF6(246)
        // temp msb, temp lsb
        byte[] data = new byte[2];
        device.read(OUT_MSB, data, 0, data.length);

        // Convert the data
        int UT = UINT(data[0], data[1]);

        // Callibration for Temperature
        int X1 = ((UT - AC6) * AC5) >> 15;
        int X2 = (MC << 11) / (X1 + MD);
        int B5 = X1 + X2;

        return B5;
    }

    public double readTemperature() throws IOException, InterruptedException {
        return ((readAndCalcB5() + 8) >> 4) / 10.0;
    }

    public static double convertToFahrenheit(double c) {
        return c * 1.8 + 32.0;
    }

    public double readPressure() throws IOException, InterruptedException {
        // Select measurement control register
        // Enable pressure measurement
        device.write(CTRL_MEAS, (byte)(CMD_READPRESSURE + (mode << 6)));
        switch (mode) {
        case ULTRALOWPOWER:
            TimeUnit.MILLISECONDS.sleep(5);
            break;
        case STANDARD:
            TimeUnit.MILLISECONDS.sleep(8);
            break;
        case HIGHRES:
            TimeUnit.MILLISECONDS.sleep(14);
            break;
        default:
            TimeUnit.MILLISECONDS.sleep(26); // ULTRAHIGHRES mode
            break;
        }

        // Read 3 bytes of data from address 0xF6(246)
        // pres msb1, pres msb, pres lsb
        byte[] data = new byte[3];
        device.read(OUT_MSB, data, 0, data.length);

        int UP = UINT(data[0], data[1], data[2]) >> (8 - mode);

        // Calibration for Pressure
        int B6 = readAndCalcB5() - 4000;
        int X1 = (B2 * (B6 * B6) >> 12) >> 11;
        int X2 = (AC2 * B6) >> 11;
        int X3 = X1 + X2;
        int B3 = (((AC1 * 4 + X3) << mode) + 2) / 4;

        X1 = (AC3 * B6) >> 13;
        X2 = (B1 * ((B6 * B6) >> 12)) >> 16;
        X3 = ((X1 + X2) + 2) >> 2;
        int B4 = (AC4 * (X3 + 32768)) >> 15;
        int B7 = (UP - B3) * (50000 >> mode);

        int p = B7 < 0x80000000 ? (B7 * 2) / B4 : (B7 / B4) * 2;

        X1 = (p >> 8) * (p >> 8);
        X1 = (X1 * 3038) >> 16;
        X2 = (-7357 * p) >> 16;
        p = p + ((X1 + X2 + 3791) >> 4);

        return p / 100.0;
    }

    public void setStandardSeaLevelPressure(double standardSeaLevelPressure) {
        this.standardSeaLevelPressure = standardSeaLevelPressure;
    }

    public double readAltitude() throws IOException, InterruptedException {
        // Calculates the altitude in meters
        double pressure = readPressure();
        return 44330.0 * (1.0 - Math.pow(pressure / standardSeaLevelPressure, 0.1903));
    }

    private int INT(byte msb, byte lsb) {
        int hi = msb & 0xFF;
        return ((hi > 127 ? hi - 256 : hi) << 8) + (lsb & 0xFF);
    }

    private int UINT(byte msb, byte lsb) {
        return ((msb & 0xFF) << 8) + (lsb & 0xFF);
    }

    private int UINT(byte msb, byte lsb, byte xlsb) {
        return ((msb & 0xFF) << 16) + UINT(lsb, xlsb);
    }
}

Compile and run

Place the above two files in a directory of your choice and compile.

pi@raspberrypi:~ $ pi4j -c BMP180Demo.java
--------------------------------------------
Pi4J - Compiling: BMP180Demo.java
--------------------------------------------
+ javac -classpath '.:classes:*:classes:/opt/pi4j/lib/*' -d . BMP180Demo.java
pi@raspberrypi:~ $

After compiling, execute it with root privileges. The temperature, barometric pressure, and altitude data are read and displayed every second.

For altitude, by specifying the sea level pressure in advance, the value is calculated based on the pressure difference from the measurement site. If no sea level is specified, the average annual sea level in Tokyo will be used as the default. On days when the atmospheric pressure is high, it may sink below the sea level.

Enter Ctrl + C to exit the program.

pi@raspberrypi:~ $ sudo pi4j BMP180Demo
+ java -classpath '.:classes:*:classes:/opt/pi4j/lib/*' BMP180Demo
Last valid input: Sun May 13 20:29:44 JST 2018
Temperature: 23.70 C (74.7 F)
Pressure   : 1000.57 hPa
Altitude   : 110.92 m

Last valid input: Sun May 13 20:29:45 JST 2018
Temperature: 23.60 C (74.5 F)
Pressure   : 1000.73 hPa
Altitude   : 109.57 m

Last valid input: Sun May 13 20:29:46 JST 2018
Temperature: 23.60 C (74.5 F)
Pressure   : 1000.57 hPa
Altitude   : 110.75 m

Last valid input: Sun May 13 20:29:47 JST 2018
Temperature: 23.60 C (74.5 F)
Pressure   : 1000.69 hPa
Altitude   : 110.08 m

^Cpi@raspberrypi:~ $

Precautions when using the program

All you need to actually use the program is the BMP180.java file. The method of creating an instance and the methods that can be used are as follows.

Create an instance

There are three ways to specify the parameters of the constructor of class BMP180 as follows.

BMP180 bmp180 = new BMP180();                         //I2C bus 1, address 0x77, operating in Standard mode

BMP180 bmp180 = new BMP180(BMP180.ULTRAHIGHRES);      //I2C bus 1, address 0x77, operating in Ultra High Resoluion mode

BMP180 bmp180 = new BMP180(2, 0x01, BMP180.STANDARD); //Operates in I2C bus 2, address 0x01, Standard mode

To specify the mode, specify one of the following.

BMP180.ULTRALOWPOWER   //Ultra Low Power mode
BMP180.STANDARD        //Standard mode
BMP180.HIGHRES         //High Resolution mode
BMP180.ULTRAHIGHRES    //Ultra High Resolution mode

Method

The following methods are available.

Methods and features
public double readTemperature() throws IOException, InterruptedException
It reads the temperature from the sensor and returns that value. The unit is degrees (Celsius).
public static double convertToFahrenheit(double c)
If you specify a degree (Celsius) value for the parameter, it will be converted to Fahrenheit and returned.
public double readPressure() throws IOException, InterruptedException
It reads the barometric pressure from the sensor and returns that value. The unit is hPa (hectopascal).
public void setStandardSeaLevelPressure(double standardSeaLevelPressure)
Set the sea level pressure in preparation for measuring altitude. The unit is specified in hPa (hectopascal).
public double readAltitude() throws IOException, InterruptedException
The altitude is returned based on the air pressure read from the sensor and the preset sea level pressure. The unit is m (meter).

Recommended Posts

Read barometric pressure and temperature with Java from Raspberry Pi 3 & BMP180
Read temperature / humidity with Java from Raspberry Pi 3 & DHT11
Display characters on I2C 1602 LCD with Raspberry Pi 3 & Java
Find the address class and address type from the IP address with Java
Correct the character code in Java and read from the URL
Convert Excel to Blob with java, save it, read it from DB and output it as a file!
Graph the sensor information of Raspberry Pi in Java and check it with a web browser
Search and execute method by name from instance with processing (java)
Process the motion detected by the motion sensor HC-SR501 with Raspberry Pi 3 & Java
Display user-defined characters on the I2C 1602 LCD with Raspberry Pi 3 & Java
Easily monitor the indoor environment-⑩ Obtain temperature / humidity / atmospheric pressure from BME280 (substitute) with Java (I2C / Pi4J)-
Use java with MSYS and Cygwin
Install Java and Tomcat with Ansible
Use JDBC with Java and Scala.
Output PDF and TIFF with Java 8
Work with Google Sheets from Java
Encrypt with Java and decrypt with C #
Find the address class and address type from the IP address with Java [No. 2 decoction]
[Java] Get Json from URL and handle it with standard API (javax.script)
Using Java with AWS Lambda-Implementation Tips-Get Instance Name from Reagion and Instance ID