プログラミング」カテゴリーアーカイブ

GitHubに公開したAndroidアプリ「BleSwitch」について(技術解説)

撮影タイミングは実機テストに使った端末に合わせて調整しています。

Android9.x(実機はGalaxyJ6+)ではLOW_LATENCYモードで100ms毎スキャンします。
それ以外のバージョンではLOW_ENERGYモードで起動します。2000msでスキャン3000ms休止のくり返しになります。実機ではスキャンできなかったり一回のボタン操作にもかかわらず2回以上信号を受けてスマホアプリを起動してしまうことがありました。

GitHubに公開したAndroidアプリ「BleSwitch」について(使い方)
ここに、こう書いていますが、少し違っていました。かなりかも。

スマホ(受信側)のBLEスキャン間隔はというと、以下にようになっています。

  • 低遅延モードLOW_LATENCY:常時スキャン
  • 低消費電力モードLOW_ENERGY:400msスキャン後4600ms休止、その繰り返し
  • バランスモードBALANCE:2000msスキャン後3000ms休止、その繰り返し

一方、BLEビーコンデバイス(発信側)の送信間隔はどうでしょう。
標準ビーコンで100ms、「Bluetoothリモートシャッター」は34msのようです(実測した結果)。

それではPochiruEcoはというと、こちらは特殊で標準ビーコンの規格を満たしていないようです。元々常時発信するデバイスではないのでそれでいいんでしょうね。ボタンが押されると800ms間隔で送信しつづけ、全体で20秒弱で停止します。

「実機ではスキャンできなかったり」の動作

上に紹介したスキャンモードのうち、低消費電力モードの場合5秒間のうち400msしかスキャンしないので、タイミングによっては、800ms間隔の送信を取りこぼす可能性があるようです。これで「実機ではスキャンできなかったり」の動作が説明できます。

「一回のボタン操作にもかかわらず2回以上信号を受けてスマホアプリを起動してしまうこと」

これについては、もう少し複雑な事情がありそうです。BLEのAPIライブラリの違いなのか、BLEビーコンデバイスとの相性の違いなのか不明ですが、BLEスキャンでデバイス検出できたあとの動作に微妙な違いが現れました。

コードで示すと


    // Device gatt callback.
    private BluetoothGattCallback leGattCallback = new BluetoothGattCallback() {
//        @Override
//        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//            Log.d(TAG, "Bluetooth GATT Status: " + status);
//       }
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.d(TAG, "Bluetooth GATT Status: " + status);
            switch(newState){
                case BluetoothGatt.STATE_CONNECTED:
                    Log.d(TAG, "Bluetooth GATT State: CONNECTED");
                    gatt.disconnect();
                    break;
                case BluetoothGatt.STATE_DISCONNECTED:
                    Log.d(TAG, "Bluetooth GATT State: DISCONNECTED");
                    gatt.close();
                    break;
                default:
                    Log.d(TAG, "Bluetooth GATT State: " + newState);
                    break;
            }
        }
    };

の部分の話になります。

スキャン検出後、Gattに接続要求して、接続したら切断して、切断したらクローズして、というようなことをやって、BLEビーコンの発信を中断させるようにしています。上記コードには「接続したら切断して、切断したらクローズして」の部分が見えると思います。

ところが、実機によっては(もしかするとSDKバージョンによっては、かもですが)これがうまく動作しないものがあります。「Gattに接続要求して、接続したら切断して」の部分で接続時のコールバックがなく、いきなり切断時のコールバックになってしまいます。結局切断してクローズするのですから「どっちでもいいじゃない!」となりそうですが、このとき結構なタイムラグがあり、それがスキャン時の複数回検出に繋がっているのかと考えています。

BLEビーコンが発信を中断してくれさえすれば、5秒間隔位でボタンを押してもその都度スマホアプリが起動できますが、中断しないと、「一回のボタン操作にもかかわらず2回以上信号を受けてスマホアプリを起動してしまうこと」になってしまいます。

Android9.xの実機ではうまく動作しますが、Android7.xの実機では上記の通りの現象が出ています。

ただ、このあたりは、BLEビーコンの動作仕様といかに合わせるかの問題になるので、あまり厳密に考えても意味なさそうです。

たとえば、ボタン操作から20秒弱で発信停止するのあれば、最初からGatt接続しないで、20秒まってからスキャン再開すればよさそうです。もちろんその間ボタン操作は効かなくなるので、そうしたやりかたで要件を満足させられるのかチェックする必要はありますが。

Android9.xの実機(GalaxyJ6+)ではうまく動作しており、ボタン操作の取りこぼしなく、操作後1秒以内にカメラアプリが起動して撮影できることを確認しています。

「THETAVプラグインで市販のBLEボタンをリモコンにしてみた」はAndroid7.x???

参考にさせていただいたリンクに「THETAVプラグインで市販のBLEボタンをリモコンにしてみた」がありますが、開発環境で取得できるログ(LogCat)を比較してみると、どうも、Android7.1.1搭載の実機と同じ動きをするようです。
上のコードでいうと「CONNECTED」にコールバックしないでいきなり「DISCONNECTED」になりますね。参考記事の中でもある程度インターバル(記事中では8秒の設定)を置かないと連続撮影してしまうとか。
BLEの機器なのかソフトなのかの微妙な違いが動作に影響するようです。

関連する記事・ページ

GitHubに公開したAndroidアプリ「BleSwitch」について(技術解説)
GitHubに公開したAndroidアプリ「BleSwitch」について(使い方)
GitHubに公開したAndroidアプリ「BleSwitch」について(概要)

お世話になったリンク

Android 5.0におけるBLE について – 受信編 –
THETA プラグインで 市販の BLE ボタンをリモコンにしてみた
iBeaconとは

以上です。

GitHubに公開したAndroidアプリ「BleSwitch」について(使い方)

使い方

設定

設定メニュにて、指定できる項目は以下の通りです。

  • デバイス名(例 PochiruEco0099999)
  • デバイスアドレス(例 12:34:56:78:9A:BC)
  • サービスUUID(省略時はb3b36901-50d3-4044-808d-50835b13a6cd(*1)

*1:このサービスUUIDは「PochiruEco」固有のものです。

機能

本アプリはBLEデバイスのボタン操作をスマホアプリに橋渡しするアプリです。BLEデバイスをスマホアプリの起動スイッチとして使うことが可能になります。

例えば、公開コードでは下記のようにしています。

  • BLEデバイスのボタンをONする
  • 本アプリが察知して設定しておいたスマホアプリを起動する
  • 起動されたスマホアプリが何かする(公開コードではカメラアプリが起動する)

全体の流れ

ステップ1: 設定画面にて、BLEデバイスを特定するための情報を設定する

①右上にある縦に点が並んだところをタッチする
clip-20191006163235.jpg
②各パラメータを指定する
clip-20191006163451.jpg
③テーマをダークにすれば、メイン画面でパラメータを隠すことができる
clip-20191006163722.jpg

ステップ2: 「Start service」ボタンをタッチすると、スマホにサービスとして登録される

④「Stop service」に変わる
clip-20191006163828.jpg
⑤サービスの状態はこんな感じ(開発者モードで確認)だが、確認する必要はない
clip-20191006163609.jpg

サービスとして登録されると、バックグラウンドで以下を行います:

  • BLEデバイスをスキャンする
  • 設定画面で設定した内容に沿ったBLEデバイスからの発信が見つかると、スキャンを中止してBLEデバイスに接続してアドバタイジングを中断させる。
  • スマホアプリを起動する。
  • BLEデバイスをスキャンを再開する

公開コードにて呼び出しているスマホアプリ

  • インテント(package): com.krasavkana.android.decoycamera
  • インテント(activity): com.krasavkana.android.decoycamera.CameraActivity
  • インテント呼び出し時に渡すパラメータ: (Key)ble-command (Value)Shoot!Shoot!

Androidのバージョンによる動作の違い

⑥Foregroundサービスとして登録されると通知にアイコンが現れる。解除されると消える
clip-20191006164117.jpg
⑦通知をみたところ。公開サンプルではメディアとして登録した
clip-20191006164207.jpg

Android9.xではForegroundサービスとして登録しています。それ以前のバージョンでは通常のサービスとして登録しています。

ステップ3: スマホアプリ(公開コードではカメラアプリ)が起動して5秒以内に撮影する

⑧スマホアプリ起動前の画面例
clip-20191006170148.jpg
⑨スマホアプリ起動後の画面例。ファインダ(右上)とシャッターアイコン(左下)およびメイン/フロントカメラ切り替えアイコン(右下)が見える。
clip-20200111073011.jpg

撮影タイミングは実機テストに使った端末に合わせて調整しています。

Android9.x(実機はGalaxyJ6+)ではLOW_LATENCYモードで100ms毎スキャンします。
それ以外のバージョンではLOW_ENECYモードで起動します。2000msでスキャン3000ms休止のくり返しになります。実機ではスキャンできなかったり一回のボタン操作にもかかわらず2回以上信号を受けてスマホアプリを起動してしまうことがありました。

ステップ4: 撮影完了後、スマホアプリ(公開コードではカメラアプリ)が自動で終了する

ステップ5: 「Stop service」ボタンをタッチすると、サービスが解除される

⑩Foregroundでない通常のサービスなので通知にアイコンはない
clip-20191006164457.jpg

カメラアプリについて

本アプリの公開コードにて起動しているスマホアプリですが、以下URLにて同じくGitHubにて公開しています。

https://github.com/krasavkana/android-camera2api-decoycamera

公開コードの改変について

本アプリの公開コードの公開目的はサンプルの提示です。BLEデバイスはボタン電池で長期間動作してくれるので、様々なIoT機器としてこれから世の中に出てくるでしょう。BLEデバイスを扱うアプリのコードサンプルとして参考にしていただればと思います。

じぶんでいろいろ改変したい場合は、GitHubに公開したコードで試してみてください。

https://github.com/krasavkana/android-ble-switch

公開日から時間がたつと(例えば1年以上かな?)、Android-SDKのサポート状態とギャップが出て、公開したコードやビルドスクリプト(Gradle)そのままでは動作しない場合がありますので、注意してください。

リリース用APKファイル

リリース用にビルドした署名付きAPKファイルを本ブログ配下に置いています。

androidアプリ「BleSwitch」リリース用APKファイル
androidアプリ「DecoyCamera」リリース用APKファイル
androidアプリ「MasterMute」リリース用APKファイル

GitHubの公開コードをローカル開発環境(AndroidStudio3.x)にクローンしてきてビルドしても得られるものですが、そうした環境をお持ちでない方のために用意しました。

どういう動作になるのか。興味のある方はいろいろ試してみてください。

Android9.xの実機(GalaxyJ6+)ではうまく動作しており、ボタン操作の取りこぼしなく、操作後1秒以内にカメラアプリが起動して撮影できることを確認しています。

関連する記事・ページ

GitHubに公開したAndroidアプリ「BleSwitch」について(技術解説)
GitHubに公開したAndroidアプリ「BleSwitch」について(使い方)
GitHubに公開したAndroidアプリ「BleSwitch」について(概要)

お世話になったリンク

Android 5.0におけるBLE について – 受信編 –
THETA プラグインで 市販の BLE ボタンをリモコンにしてみた
iBeaconとは

以上です。

GitHubに公開したAndroidアプリ「BleSwitch」について(概要)

ざっくりと言って何するものか?

アンドロイド版BLEスイッチという、
GitHubに公開しているAndroidアプリ(コード)について、解説します。

https://github.com/krasavkana/android-ble-switch

このアプリは、BLEビーコンデバイスをAndroidアプリの操作に使えるようにする橋渡しアプリです。

「Bluetoothリモートシャッター」というのが100均で売っています。iPhoneやAndroidデバイスの内蔵カメラを起動しておけば、このデバイスを使って離れた場所からシャッターを操作することができます。

clip-20191005115218.jpg

くり返しになりますが、本アプリは、【BLEビーコンデバイス】を【Androidアプリの操作】に使えるようにする【橋渡しアプリ】です。これを「Bluetoothリモートシャッター」の動作に対応させて説明すると、

  • 【BLEビーコンデバイス】 ⇒ 「Bluetoothリモートシャッター」
  • 【Androidアプリの操作】 ⇒ シャッターを操作する
  • 【橋渡しアプリ】 ⇒ デバイスのボタン操作をAndroidアプリのシャッター操作に橋渡しする

となります。

じゃあ、100均デバイスとの違いは?

これ、実はこの問い自体がちょっと違います。 世の中のほとんどすべてのデバイスについて言えますが、デバイス単体で動作するものはありません。デバイスと連携して動作するコード(アプリ、ソフトウエア)が必要です。

デバイス+橋渡しアプリで動作する

100均デバイスの場合は、デバイスとペアリングしてくれるサービスが【橋渡しアプリ】として機能して100均デバイスのボタン操作をAndroidアプリ(内蔵カメラアプリ)に橋渡ししてくれます。

本アプリの対応デバイスは?

本アプリの場合、アプリ自体は【橋渡しアプリ】なので、一緒に動作させる【BLEビーコンデバイス】を考える必要があります。

コード公開にあたり実機テストをしています。その際次の【BLEビーコンデバイス】を使って動作確認しています。

PochiruEco
https://www.products.braveridge.com/pochiru_eco/

この場合、

  • 【BLEビーコンデバイス】 ⇒ 「Pochiru(Eco)」
  • 【Androidアプリの操作】 ⇒ 本アプリでは専用カメラアプリを起動する
  • 【橋渡しアプリ】 ⇒ 本アプリのこと

として動作します。

100均デバイス+橋渡しアプリとの違いは?

漸く、元の話題に戻ってきました。100均デバイス+橋渡しアプリとの違いは、下記3点です。

  • ペアリング不要
  • Androidデバイス内のアプリを起動する
  • ビーコン(BLE)を認識できる

ペアリング不要

ペアリングする場合、デバイスとペアリング認証する必要があり、同時に複数のAndroidアプリを同時に動作させることができません。

これはメリットにもなりデメリットにもなります。想定するものに依って変わります。

ペアリングしていない場合、スマホアプリからはデバイス個体を見分けることができないため、すぐ近くで同じ種類の100均デバイスが使われるとそれに反応してしまうという困ったことが起きる可能性があります。
逆に、ペアリングしている場合、ひとつのデバイスで複数台のスマホアプリを同時に処理させることができません。

本アプリの場合、複数台のスマホアプリを同時に起動できます。

例えば、複数台のスマホ内蔵カメラを同時に起動することができます。シャッターのタイミングは、スマホ内蔵カメラをハンドリングするカメラアプリが担当するので何とも言えませんが、ほぼ同時に処理することができます。

なので、同じ被写体に向かって前後左右上下の6台のスマホを用意すれば、同時に6方向からの画像を撮影することが可能になります。映画「マトリックス」のような一瞬を捉えた連続画像ができそうです。
#ひとりでスマホを6台用意するのは大変でしょうね。できれば同じもの6台にした方がタイミング合わせのためにもいいですね。

アプリを起動

「Bluetoothリモートシャッター」の場合にできる操作は、離れた場所からシャッターを切る、でした。これには事前にスマホの内蔵カメラアプリを起動しておくなどの操作が必要です。

本アプリの場合、そうした準備は不要で、スマホアプリ自体を起動することができます。

公開したコードサンプルでは、拙作のカメラアプリを起動しています。起動されたカメラアプリは起動したらすぐにシャッターを切ります。暗い場所やフォーカスしにくい被写体などはピンぼけになるかも知れませんけどね。

BLEビーコンを認識

本アプリでは、ペアリングは行わず、ビーコンの送信(アドバタイジング)をスキャンします。デバイス名やデバイスアドレス、サービスUUID等のパラメータを指定することにより、周囲にある複数のビーコンから指示を待つべきビーコンを絞ります。

デバイス個体を特定するパラメータ指定がなければ、その時初めて遭遇したBLEビーコンに反応することになります。あまりそうした使い方は想定していませんが、それはそれで使い道があるかも知れません。アイデア次第ですね。

本アプリの公開コードで使用した【BLEビーコンデバイス】である「Pochiru(Eco)」の場合、標準的なビーコン(iBeaconやEdyStoneなど)の動作仕様とはかなり違った動きをするようです。

たとえば、標準ビーコンは100ms毎に発信するのに比べ、このデバイスは800ms毎の発信であるとか。標準ビーコンは電池が切れるまで何か月でも連続発信するけど、このデバイスはボタンをONして20秒弱しか発信しないだとか。標準ビーコンは電池が入って切れるまで動作するので、元々発信ボタンが必要ないですよね。

本アプリでは、こうした標準ビーコンの動作仕様を想定していません。コードを書き換えればそういうものも認識させることができます。認識させることができれば、お店に設置してある標準ビーコンを察知してスマホ等に通知するとか、お年寄りや子供にポータブルな標準ビーコンを携帯してもらっておいて、近づいたり遠ざかったりしたらスマホに通知するとか、ができるようになるでしょう。

関連する記事・ページ

GitHubに公開したAndroidアプリ「BleSwitch」について(技術解説)
GitHubに公開したAndroidアプリ「BleSwitch」について(使い方)
GitHubに公開したAndroidアプリ「BleSwitch」について(概要)

お世話になったリンク

Android 5.0におけるBLE について – 受信編 –
THETA プラグインで 市販の BLE ボタンをリモコンにしてみた
iBeaconとは

以上です。

【コードサンプル】ネット転送量モニタ

ネットワーク転送量モニタのScript例

hh55_network_transfer.ps1

  • hh55_xxxなので、heartbeat.ps1が直接起動する
  • 毎時55分になると実行する。hh30にすると毎時30分になるし、hh70 にすると実行されることはなくなる。
  • 毎時15分、毎時45分に同じタスクを追加したければ、このScriptファイルをCOPYしてファイル名を hh15_network_transfer.ps1 、hh45_network_transfer.ps1 に変更すればいい
  • ネットワークアダプタが複数ある場合、下記サンプルのように、モニタするネットワークアダプタを絞りこむこともできる
  • 履歴ファイルをCSVファイル形式にしているので、Excel等他のアプリで容易に読み込み可能。
# 定数定義
Set-Variable -Name 'NetTransHist' -Value history\dd9999_network_transfer_history.txt
Set-Variable -Name 'NetAdapterName' -Value "イーサネット"
# ネットの容量を確認する
$now = Get-Date -Format G
$rcv = (get-netadapterStatistics -Name $NetAdapterName).ReceivedBytes
$snd = (get-netadapterStatistics -Name $NetAdapterName).SentBytes
echo "$now,$rcv,$snd" | Out-File -FilePath $NetTransHist -Append

関連する記事・ページ

自宅PCをスマホでリモート管理(タスク管理編)
PCをリモート管理する1手法
【コードサンプル】Windowsタスクへの登録例
【コードサンプル】heartbeat.ps1 – 心音確認 PowerShell Script
【コードサンプル】1時間タスクのScript例
【コードサンプル】24時間タスクのScript例
【コードサンプル】hbJob.bat – Windows Bat Script
【コードサンプル】プロセスモニタのScript例
【コードサンプル】IFTTT経由でLINE通知するScript例
【コードサンプル】ネット転送量モニタ

【コードサンプル】プロセスモニタのScript例

プロセスモニタのScript例

hh30_monitor_process_ME.ps1

  • モニタの場合比較的短い周期(5分や10分)でScriptタスクを起動する必要があるので、メインの処理は hh99_xxxに置いて、修正時のインパクトが少なくなるようにしている
#$scriptName = $myInvocation.MyCommand.name
#************************************************************************************
# 定数定義
#************************************************************************************
Set-Variable -Name 'trigCommand' -Value <IFTTTのEventName>
#************************************************************************************
# メイン処理
#************************************************************************************
.\hh99_monitor_process.ps1 hh99_monitor_process_ME.ps1 mediaespresso PC_A

hh99_monitor_process.ps1

  • ファイルに保存しておいた前回モニタ時のCPU時間を取得して、現在のCPU時間を比較する。差が2秒以内なら「停止状態」としている
#
# プロセスが起動していてCPUを消費しているか監視(モニタ)する
#
#************************************************************************************
# パラメータ定義
#************************************************************************************
Param(
    [parameter(Mandatory=$True)][string]$ScriptName,
    [parameter(Mandatory=$True)][string]$ProcessName,
    [parameter(Mandatory=$True)][string]$MachineName
)
#************************************************************************************
# 定数定義
#************************************************************************************
if ((Get-Variable  -ErrorAction silentlyContinue -Name 'trigCommand' | Measure-Object).count -eq 0){
Set-Variable -Name 'trigCommand' -Value <IFTTTのEventName>
}
#************************************************************************************
# 関数定義
#************************************************************************************
#************************************************************************************
# メイン処理
#************************************************************************************
$vFile =$ScriptName.Replace(".ps1",".txt")
echo "$vFile at $(Get-Date -Format G)"

$cnt = (get-process -ErrorAction SilentlyContinue $ProcessName | Measure-Object).count

if($cnt -eq 0){
# プロセスが見つからない
    $now = Get-Date
    echo "プロセスが起動していません at $now"
    exit
}
#echo ProcessName &"(" & pID &")"

Get-Content $vFile | Foreach-Object{
   $var = $_.Split('=')
   Set-Variable -Name $var[0] -Value $var[1]
}

echo "CPU:$cpu"

$cpuAfter = 0
((Get-Process -ErrorAction SilentlyContinue $ProcessName).CPU | ForEach-Object { $cpuAfter = $cpuAfter + $_})
$cpuAfter = [int]$cpuAfter

echo "CPUAFTER:$cpuAfter"

# フラグファイルを生成するだけにする(IFFFへMAKERイベント発火)
if ($cpu -gt $cpuAfter){ # 初期化する
    echo "CPU=$cpuAfter" | Out-File -FilePath $vFile
}else{
    if ($cpu -gt ($cpuAfter - 2)){ # 誤差があるので2とした
        .\hh99_IFTTT_trig.ps1 $trigCommand "アイドル状態($MachineName)"
    }else{
        echo "CPU=$cpuAfter" | Out-File -FilePath $vFile
    }
    echo "CPU=$cpuAfter" | Out-File -FilePath $vFile
}