com.ryebrye.releaser.weathersensors.BMP180Sensor.java Source code

Java tutorial

Introduction

Here is the source code for com.ryebrye.releaser.weathersensors.BMP180Sensor.java

Source

package com.ryebrye.releaser.weathersensors;

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * Created by ddcryang on 2/4/15.
 * <p/>
 * Some code was taken from the RaspberryPI project
 * https://code.google.com/p/raspberry-pi4j-samples/
 * and re-used under the MIT license.
 *
 */
@Profile("weatherSensorEquipped")
@Component
public class BMP180Sensor implements TemperatureSensor, BarometricPressureSensor {
    private static final Logger log = LoggerFactory.getLogger(BMP180Sensor.class);
    /*
    Prompt> 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
     */
    // This next addresses is returned by "sudo i2cdetect -y 1", see above.
    public final static int BMP180_ADDRESS = 0x77;
    // set to false if for some reason the BMP180 is not big_endian
    private static final boolean BIG_ENDIAN = true;

    private boolean verbose = false;
    private I2CBus bus;
    private I2CDevice bmp180;

    // conversion factor 0.0002952998751  Pa = inHg
    public final static double PA_TO_IN_HG_CONVERSION_FACTOR = 0.0002952998751;

    // highest precision mode
    private OperatingMode mode = OperatingMode.ULTRAHIGHRES;

    // BMP180 Registers
    public final static int BMP180_CAL_AC1 = 0xAA; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_AC2 = 0xAC; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_AC3 = 0xAE; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_AC4 = 0xB0; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_AC5 = 0xB2; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_AC6 = 0xB4; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_B1 = 0xB6; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_B2 = 0xB8; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_MB = 0xBA; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_MC = 0xBC; // R   Calibration data (16 bits)
    public final static int BMP180_CAL_MD = 0xBE; // R   Calibration data (16 bits)
    public final static int BMP180_CONTROL = 0xF4;
    public final static int BMP180_TEMPDATA = 0xF6;
    public final static int BMP180_PRESSUREDATA = 0xF6;
    public final static int BMP180_READTEMPCMD = 0x2E;
    public final static int BMP180_READPRESSURECMD = 0x34;

    // calibration values that are read before doing anything else
    private final int cal_AC1;
    private final int cal_AC2;
    private final int cal_AC3;
    private final int cal_AC4;
    private final int cal_AC5;
    private final int cal_AC6;
    private final int cal_B1;
    private final int cal_B2;
    private final int cal_MB;
    private final int cal_MC;
    private final int cal_MD;

    public BMP180Sensor() {
        try {
            // Get i2c bus
            bus = I2CFactory.getInstance(I2CBus.BUS_1);
            log.debug("Connected to bus. OK.");

            // Get device itself
            bmp180 = bus.getDevice(BMP180_ADDRESS);
            log.debug("Connected to device. OK.");
        } catch (IOException e) {
            log.error("Couldn't connect to the BMP180 device ");
            throw new RuntimeException("Unable to connect to BMP180", e);
        }

        cal_AC1 = readS16(BMP180_CAL_AC1); // INT16
        cal_AC2 = readS16(BMP180_CAL_AC2); // INT16
        cal_AC3 = readS16(BMP180_CAL_AC3); // INT16
        cal_AC4 = readU16(BMP180_CAL_AC4); // UINT16
        cal_AC5 = readU16(BMP180_CAL_AC5); // UINT16
        cal_AC6 = readU16(BMP180_CAL_AC6); // UINT16
        cal_B1 = readS16(BMP180_CAL_B1); // INT16
        cal_B2 = readS16(BMP180_CAL_B2); // INT16
        cal_MB = readS16(BMP180_CAL_MB); // INT16
        cal_MC = readS16(BMP180_CAL_MC); // INT16
        cal_MD = readS16(BMP180_CAL_MD); // INT16
        log.debug(
                "BMP180 Calibration data: AC1: {}, AC2: {}, AC3: {}, AC4: {}, AC5: {}, AC6: {}, B1: {}, B2: {}, MB: {}, MC: {}, MD: {}",
                cal_AC1, cal_AC2, cal_AC3, cal_AC4, cal_AC5, cal_AC6, cal_B1, cal_B2, cal_MB, cal_MC, cal_MD);

    }

    protected static void waitfor(long howMuch) {
        try {
            Thread.sleep(howMuch);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
    }

    public int readRawTemp() throws Exception {
        // "Reads the raw (uncompensated) temperature from the sensor"
        bmp180.write(BMP180_CONTROL, (byte) BMP180_READTEMPCMD);
        waitfor(5); // Wait 5ms
        int raw = readU16(BMP180_TEMPDATA);
        log.debug("DBG: Raw Temp: " + (raw & 0xFFFF) + ", " + raw);
        return raw;
    }

    public int readRawPressure() throws Exception {
        // "Reads the raw (uncompensated) pressure level from the sensor"
        bmp180.write(BMP180_CONTROL, (byte) (BMP180_READPRESSURECMD + (this.mode.value() << 6)));
        waitfor(this.mode.getPressureWaitMillis());
        int msb = bmp180.read(BMP180_PRESSUREDATA);
        int lsb = bmp180.read(BMP180_PRESSUREDATA + 1);
        int xlsb = bmp180.read(BMP180_PRESSUREDATA + 2);
        int raw = ((msb << 16) + (lsb << 8) + xlsb) >> (8 - this.mode.value());
        log.debug("DBG: Raw Pressure: " + (raw & 0xFFFF) + ", " + raw);
        return raw;
    }

    @Override
    public double readTemperature() {
        // "Gets the compensated temperature in degrees celcius"
        int UT = 0;
        int X1 = 0;
        int X2 = 0;
        int B5 = 0;
        double temp = 0.0f;

        // Read raw temp before aligning it with the calibration values
        try {
            UT = this.readRawTemp();
        } catch (Exception e) {
            throw new RuntimeException("error reading raw temp", e);
        }
        X1 = ((UT - this.cal_AC6) * this.cal_AC5) >> 15;
        X2 = (this.cal_MC << 11) / (X1 + this.cal_MD);
        B5 = X1 + X2;
        temp = ((B5 + 8) >> 4) / 10.0f;
        if (verbose) {
            System.out.println("DBG: Calibrated temperature = " + temp + " C");
        }
        return temp;
    }

    @Override
    public double readPressureAsInHg() {
        try {
            return readPressureAsPa() * PA_TO_IN_HG_CONVERSION_FACTOR;
        } catch (Exception e) {
            throw new RuntimeException("Couldn't read pressure");
        }
    }

    //
    public double readPressureAsPa() throws Exception {
        // "Gets the compensated pressure in pascal"
        int UT = 0;
        int UP = 0;
        int B3 = 0;
        int B5 = 0;
        int B6 = 0;
        int X1 = 0;
        int X2 = 0;
        int X3 = 0;
        int p = 0;
        int B4 = 0;
        int B7 = 0;

        UT = this.readRawTemp();
        UP = this.readRawPressure();

        // True Temperature Calculations
        X1 = (int) ((UT - this.cal_AC6) * this.cal_AC5) >> 15;
        X2 = (this.cal_MC << 11) / (X1 + this.cal_MD);
        B5 = X1 + X2;
        if (log.isTraceEnabled()) {
            log.trace("X1 = " + X1);
            log.trace("X2 = " + X2);
            log.trace("B5 = " + B5);
            log.trace("True Temperature = " + (((B5 + 8) >> 4) / 10.0) + " C");
        }
        // Pressure Calculations
        B6 = B5 - 4000;
        X1 = (this.cal_B2 * (B6 * B6) >> 12) >> 11;
        X2 = (this.cal_AC2 * B6) >> 11;
        X3 = X1 + X2;
        B3 = (((this.cal_AC1 * 4 + X3) << this.mode.value()) + 2) / 4;
        if (log.isTraceEnabled()) {
            log.trace("B6 = " + B6);
            log.trace("X1 = " + X1);
            log.trace("X2 = " + X2);
            log.trace("X3 = " + X3);
            log.trace("B3 = " + B3);
        }
        X1 = (this.cal_AC3 * B6) >> 13;
        X2 = (this.cal_B1 * ((B6 * B6) >> 12)) >> 16;
        X3 = ((X1 + X2) + 2) >> 2;
        B4 = (this.cal_AC4 * (X3 + 32768)) >> 15;
        B7 = (UP - B3) * (50000 >> this.mode.value());
        if (log.isTraceEnabled()) {
            log.trace("X1 = " + X1);
            log.trace("X2 = " + X2);
            log.trace("X3 = " + X3);
            log.trace("B4 = " + B4);
            log.trace("B7 = " + B7);
        }
        if (B7 < 0x80000000) {
            p = (B7 * 2) / B4;
        } else {
            p = (B7 / B4) * 2;
        }

        if (verbose) {
            System.out.println("DBG: X1 = " + X1);
        }

        X1 = (p >> 8) * (p >> 8);
        X1 = (X1 * 3038) >> 16;
        X2 = (-7357 * p) >> 16;
        if (log.isTraceEnabled()) {
            log.trace("p  = " + p);
            log.trace("X1 = " + X1);
            log.trace("X2 = " + X2);
        }
        p = p + ((X1 + X2 + 3791) >> 4);
        if (log.isTraceEnabled()) {
            log.trace("Pressure = " + p + " Pa");
        }

        return p;
    }

    /**
     * Represent the various operating modes that the BMP180 has available
     * <p/>
     * see the data sheet for more information.
     */
    public enum OperatingMode {
        ULTRALOWPOWER(0, 5), STANDARD(1, 8), HIGHRES(2, 14), ULTRAHIGHRES(3, 26);

        private int mode;
        private int pressureWaitMillis;

        OperatingMode(int mode, int pressureWaitMillis) {
            this.mode = mode;
            this.pressureWaitMillis = pressureWaitMillis;
        }

        public int value() {
            return this.mode;
        }

        public int getPressureWaitMillis() {
            return pressureWaitMillis;
        }
    }

    private int readU8(int reg) {
        // "Read an unsigned byte from the I2C device"
        int result = 0;
        try {
            result = this.bmp180.read(reg);
            log.trace("I2C: Device {} returned {} from reg {}", BMP180_ADDRESS, result, reg);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        return result;
    }

    private int readS8(int reg) {
        // "Reads a signed byte from the I2C device"
        int result = 0;
        try {
            result = this.bmp180.read(reg);
            if (result > 127) {
                result -= 256;
            }
            log.trace("I2C: Device {} returned {} from reg {}", BMP180_ADDRESS, result, reg);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        return result;
    }

    private int readU16(int register) {
        try {
            int hi = this.readU8(register);
            int lo = this.readU8(register + 1);
            return (BIG_ENDIAN) ? (hi << 8) + lo : (lo << 8) + hi;
        } catch (Exception e) {
            throw new RuntimeException("error readingU16", e);
        }
    }

    private int readS16(int register) {
        try {
            int hi = 0, lo = 0;
            if (BIG_ENDIAN) {
                hi = this.readS8(register);
                lo = this.readU8(register + 1);
            } else {
                lo = this.readS8(register);
                hi = this.readU8(register + 1);
            }
            return (hi << 8) + lo;
        } catch (Exception e) {
            throw new RuntimeException("error readingS16", e);
        }

    }

}