#はじめに
Raspberry Pi 3 を使って、気圧・温度センサー BMP180 から気圧および温度情報を読み取ります。また海面気圧を指定すると、測定した気圧をもとに測定地点の高度を算出します。プログラミング言語は Java を使用しています。
BMP180 |
---|
#準備
Raspberry Pi の GPIO を Java から制御するためには Pi4J を使用します。Pi4J の環境については、以下の記事の「Pi4J のインストール」をご参照ください。
またこのセンサーは I2C で通信を行うため、Raspberry Pi の I2C 通信機能を有効にしておきます(デフォルトでは無効になっています)。
Raspbian の GUI から有効にする場合は、タスクバーの「Menu」ボタンから「設定」~「Raspberry Pi の設定」で設定パネルを開き、「インターフェイス」タブを選んで「I2C」を有効にします。コマンドラインから有効にする場合は、sudo raspi-config コマンドを入力し、「Interfacing Options」~「I2C」と選んで、最後に「Yes」を選択します。
設定した後は、念のためにリブートしておきます。
Raspberry Pi と BMP180 は以下のように接続しました。
#センサーの動作モード
このセンサーは、気圧を読み取る際に4つのモードのいずれかを指定して読み取ります。指定されるモードによって、サンプリング数(精度)と消費電力などが異なってきます。このモードは気圧の読み取り時のみに有効で、温度の読み取りに対しては無関係です。
(参考)BMP180 データシート
#プログラム
##構成
プログラムは、以下の2つのファイルから構成されています。
BMP180Demo.java
BMP180.java
センサーのアドレスはデフォルトの 77H を想定しています。センサーを接続した後以下のコマンドを入力し、77H 以外のところで認識されている場合は、BMP180.java の中で定義している定数 I2C_ADRESS にその値を設定してください。
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:~ $
##ソースコード
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);
}
}
}
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);
}
}
#コンパイルと実行
上の2つのファイルを適当なディレクトリに置いてコンパイルします。
pi@raspberrypi:~ $ pi4j -c BMP180Demo.java
--------------------------------------------
Pi4J - Compiling: BMP180Demo.java
--------------------------------------------
+ javac -classpath '.:classes:*:classes:/opt/pi4j/lib/*' -d . BMP180Demo.java
pi@raspberrypi:~ $
コンパイルが終わったらルート権限で実行します。毎秒ごとに温度・気圧・高度のデータを読み出して表示します。
高度については、あらかじめ海面気圧を指定しておくことにより、計測地との気圧差で値を計算します。海面気圧が指定されていない場合、東京の年間平均の海面気圧がデフォルトとして使用されます。気圧が高い日などは、海面下に沈んだりします。
プログラムを終了する場合は Ctrl+C を入力してください。
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:~ $
#プログラム利用時の注意点など
実際にプログラムを利用する場合必要になるのは、BMP180.java ファイルのみです。インスタンスの作成方法と、利用できるメソッドは以下のとおりです。
##インスタンスの作成
クラス BMP180 のコンストラクタのパラメータは、以下の3通りの指定方法があります。
BMP180 bmp180 = new BMP180(); // I2C バス 1、アドレス 0x77、Standard モードで動作
BMP180 bmp180 = new BMP180(BMP180.ULTRAHIGHRES); // I2C バス 1、アドレス 0x77、Ultra High Resoluion モードで動作
BMP180 bmp180 = new BMP180(2, 0x01, BMP180.STANDARD); // I2C バス 2、アドレス 0x01、Standard モードで動作
モードを指定する場合は、以下のいずれかを指定します。
BMP180.ULTRALOWPOWER // Ultra Low Power モード
BMP180.STANDARD // Standard モード
BMP180.HIGHRES // High Resolution モード
BMP180.ULTRAHIGHRES // Ultra High Resolution モード
##メソッド
以下のメソッドが用意されています。
メソッドと機能 |
---|
public double readTemperature() throws IOException, InterruptedException |
|
public static double convertToFahrenheit(double c) |
|
public double readPressure() throws IOException, InterruptedException |
|
public void setStandardSeaLevelPressure(double standardSeaLevelPressure) |
|
public double readAltitude() throws IOException, InterruptedException |
|