この記事は Androidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、記事の最後で紹介します)
意外かも知れませんが、スマホには温度と湿度のセンサーをもっていません。
アプリで温度と湿度を扱う必要がある場合、BLEで通信できるSwitchBotをおススメします♪
ポイント
SwitchBot温湿度計には高精度の温度センサーと湿度センサーを搭載しています。
本体の液晶画面に表示するほか、BLE(BluetoothLowEnergy)を使って、気温と湿度をインタフェースすることが可能です。
また、温湿度計は、現在の気温と湿度をBLEアドバタイズに含めてデータ送信しています。
温湿度計から送信されているBLEアドバタイズにある気温と湿度をアプリに連携する機能を、Bluetoothライブラリで実装する方法を紹介します。
BLEコントローラ
Bluetoothデバイスと通信する場合、マニフェストファイルへの権限指定が必要です。
さらに Android12以降では、アプリ側でユーザー承認をリクエストする必要があります。
マニフェストファイルへの権限指定と権限チェックの実装については、以下の参考記事を参照ください。
BLEコントローラクラス
BLEコントローラクラスでは、BLE MACとBLEアドバタイズのタイプを指定して、受信するBLEアドバタイズのフィルタ処理を行い、目的のサービスデータを取得します。
スキャン(受信待ち状態)処理では、Handlerを使用して、タイムアウト処理を実装します。
また、インタフェースを実装して、受信したサービスデータの受け渡しを行います。
:
public class BluetoothLowEnergyController {
private static final long SCAN_PERIOD = 30000; // スキャンタイムアウト(30秒)
private final Handler handler;
private final BluetoothAdapter bluetoothAdapter;
private final BleScanCallback bleScanCallback;
private final BluetoothLeScanner bluetoothLeScanner;
private OnChangeListener onChangeListener;
private final Set<ScanResult> results = new HashSet<>();
private final List<ScanResult> batchScanResults = new ArrayList<>();
private byte[] scanData = new byte[]{0};
private Runnable runnable;
private String macAddress;
private Long updated;
private long timer;
private Byte type = null;
private final Context context;
// コンストラクタ
public BluetoothLowEnergyController(Context context) {
this.context = context;
this.handler = new Handler(Looper.getMainLooper());
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
this.bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
this.bleScanCallback = new BleScanCallback();
}
// インタフェース
public interface OnChangeListener {
void onValueChanged(byte[] value);
}
public void setOnChangeListener(final BluetoothLowEnergyController.OnChangeListener onChangeListener) {
this.onChangeListener = onChangeListener;
}
@SuppressLint("MissingPermission")
public void scan() {
timer = System.currentTimeMillis();
runnable = new Runnable() {
@Override
public void run() {
if (results.size() == 0 && System.currentTimeMillis() > timer + SCAN_PERIOD ) {
handler.removeCallbacks(runnable);
// BLEスキャン停止
bluetoothLeScanner.stopScan(bleScanCallback);
}
handler.postDelayed(this, SCAN_PERIOD);
}
};
bleScanCallback.clear();
// BLEスキャン開始
bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), bleScanCallback);
handler.post(runnable);
}
@SuppressLint("MissingPermission")
public void pause() {
handler.removeCallbacks(runnable);
bluetoothLeScanner.stopScan(bleScanCallback);
bleScanCallback.clear();
}
private List<ScanFilter> buildScanFilters() {
List<ScanFilter> scanFilters = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setDeviceAddress(macAddress);
scanFilters.add(builder.build());
return scanFilters;
}
private ScanSettings buildScanSettings() {
ScanSettings.Builder builder = new ScanSettings.Builder();
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
return builder.build();
}
private class BleScanCallback extends ScanCallback {
@SuppressLint("MissingPermission")
@Override
public void onScanResult(int callbackType, ScanResult result) {
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
results.add(result);
BluetoothDevice bluetoothDevice = result.getDevice();
scanData = result.getScanRecord().getBytes();
updated = System.currentTimeMillis();
if (type != null) {
int length;
int position = 0;
byte newType;
while (position < scanData.length) {
length = scanData[position];
if (length == 0) { break; }
position++;
newType = scanData[position];
if (newType == type) {
byte[] value = Arrays.copyOfRange(scanData, position + 1, position + length);
// 受信データ通知
onChangeListener.onValueChanged(value);
}
position = position + length;
}
} else {
handler.removeCallbacks(runnable);
//BLEスキャン停止
bluetoothLeScanner.stopScan(bleScanCallback);
}
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
batchScanResults.addAll(results);
}
@Override
public void onScanFailed(int errorCode) {
}
synchronized void clear() {
results.clear();
batchScanResults.clear();
Arrays.fill(scanData,(byte)0);
}
}
// Android端末のBluetooth機能の有効化判定
public boolean requestBluetoothFeature() {
return bluetoothAdapter.isEnabled();
}
// デバイスのMACアドレス設定 //
public void setMacAddress(String mac) { macAddress = (mac.length() > 0 && !mac.equals("none") ? mac : "00:00:00:00:00:00"); }
// BLEアドバタイズパケット取得 //
public byte[] getScanData() { return scanData; }
// BLEアドバタイズパケット取得時刻 //
public String getUpdated() {
SimpleDateFormat HHmm = new SimpleDateFormat("HH:mm", Locale.JAPAN);
Date date = new Date(updated);
return HHmm.format(date);
}
// タイプ設定 //
public void setType(Byte newType) { type = newType; }
// 値取得 //
public Set<ScanResult> getScanResult() { return results; }
public List<ScanResult> getBatchScanResults() { return batchScanResults; }
}
SwichBotインタフェース
SwitchBotインタフェースでは、BLEコントローラにSwitchBotデバイスのBLE MACとBLEアドバタイズのタイプを指定して、BLEスキャンを開始します。
SwitchBotから取得した気温と湿度はバイナリデータのため、数値変換と、気温と湿度それぞれのTextViewに数値変換した値を出力します。
バイナリデータの構造(フォーマット)は、以下の参考記事を参照ください。
また、インスタンス化したBLEコントローラを操作するためのgetterメソッドを実装します。
:
private static final String DEVICE_MAC = "CA:D4:47:66:1E:BB";
private static final byte SWITCHBOT = 0x16;
:
public BluetoothLowEnergyController getBluetoothLowEnergyController() {
return bluetoothLowEnergyController;
}
public void setBluetoothLowEnergyController(BluetoothLowEnergyController newBluetoothLowEnergyController, TextView temp, TextView hum) {
if (bluetoothLowEnergyController == null) {
bluetoothLowEnergyController = newBluetoothLowEnergyController;
bluetoothLowEnergyController.setOnChangeListener(value -> {
if (value.length > 6) {
int sign = value[6] & 0x80;
float decimals = (value[5] & 0x0f);
temperature = (sign == 0x80) ? (value[6] & 0x7f) + (decimals / 10) : (value[6] & 0x7f) + (decimals / 10) * -1;
temp.setText(String.format(context.getString(R.string.format_temperature), temperature));
if (value.length > 7) {
humidity = value[7] & 0x7f;
hum.setText(String.format(context.getString(R.string.format_humidity), humidity));
}
}
});
bluetoothLowEnergyController.setMacAddress(DEVICE_MAC);
bluetoothLowEnergyController.setType(SWITCHBOT);
bluetoothLowEnergyController.scan();
}
}
:
Fragment
Fragement(または、Activity)では、気温と湿度を表示するためのTextViewをセットし、SwitchBotインタフェースにインスタンス化したBLEコントローラとTextViewを引き渡します。
BLEコントローラは、SwitchBotから受信した気温と湿度をTextViewに一定間隔で更新します。
BLEコントローラの停止はonPauseで行い、再開はonResumeで行います。
:
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_home, container, false);
:
// 画面項目
temperature = view.findViewById(R.id.temperature);
humidity = view.findViewById(R.id.humidity);
:
return view;
}
@Override
public void onResume() {
super.onResume();
:
// SwitchBot
setBluetoothLowEnergyController(new BluetoothLowEnergyController(getActivity()), temperature, humidity);
:
}
@Override
public void onPause() {
:
getBluetoothLowEnergyController().pause();
super.onPause();
}
:
SwitchBotデバイスのBLE MACアドレス
SwitchBotデバイスのBLE MACは、SwitchBotアプリで確認できます。
SwitchBotアプリのインストールはこちらです↓↓↓
①SwitchBotアプリのマイホームから温湿度計BBをタップします。
②デバイス情報をタップします。
③デバイス情報にBLE MACが表示されます。
今回は、ここまでです。
SwitchBotから気温と湿度をBLE通信でアプリに連携している Androidアプリです。
誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にあるコメントか、
こちらから、お願いいたします♪
ポチッとして頂けると、
次のコンテンツを作成する励みになります♪
これからAndroidのアプリ開発やJavaでの開発を始めたい方へ
初めての Android のアプリ開発では、アプリケーション開発経験がない方や、
アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、
書籍などによる独学ではアプリ開発できるようになるには、
かなりの時間がかかりますので、オンラインスクールでの習得をおススメします。
未経験者からシステムエンジニアを目指すのに最適かと、まずは無料相談から♪
未経験者からプログラマーを目指すのに最適かと、まずは無料カウンセリングから♪
カリキュラムとサポートがしっかりしています、お得なキャンペーンとかいろいろやっています♪
ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適かと、まずは無料オンライン相談から♪
参考になったら、💛をポッチとしてね♪
コメント欄