◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
照度センサーを使うには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
スマホの魅力は様々なアプリケーションを動かすことができるだけでなく、いろいろなセンサーを手軽に利用できるところにもあるのではないでしょうか?。
以下では照度センサーから値を得る方法について紹介します。
なおAndroidでセンサーを扱うには、システムからセンサー管理オブジェクトを取得し、扱いたいセンサー制御オブジェクトと共にリスナーを設定する必要があります。
センサー管理オブジェクトとセンサー制御オブジェクトの取得については「利用可能なセンサーを知るには(AndroidMode編)」記事を参照してください。
【詳細】
全般的な注意
センサー管理オブジェクト、センサー制御オブジェクトの取得と、読み取りたいセンサーへのリスナー登録は、Androidの画面が準備完了となり画面が表示される直前に行うべきです。
またセンサーを適時読み取る処理はバッテリーを消費するため、必要がなくなった場合(例えば画面が隠れるような状況になった場合)は、これを即座に停止すべきでしょう。
とくにPROCESSINGでセンサーを使うなら、リスナーの登録処理はsetup関数ではなく onResumeメソッドをオーバライドして行うべきです。
またセンサーの開放処理は、onPauseメソッドをオーバライドして行うのが良いでしょう。
このあたりの話題は「PROCESSINGとライフサイクル(AndroidMode編)」記事も参考としてください。
明るさの単位にはルーメン(lm)、カンデラ(cd)、ルクス(lux)がありますが、照度センサーから得られる値の単位はルクス(lux)です。
つまり・・・スマホの照度センサーに、どのくらいの光が届いているか?(照らされているか=スマホ周辺の明るさ)を示しています。
明るさの単位については下記サイト様などが参考となります。ありがとうございます。
センサー制御オブジェクト取得
mng : センサー管理オブジェクト
Type : 取り出すセンサーの種類
Typeに取り出したいセンサーの種類を与えることで、センサー制御用のオブジェクトを取得できます。
照度センサー用の制御オブジェクトを取り出したいなら、TypeにはSensorクラスがもつTYPE_LIGHT定数(Sensor.TYPE_LIGHT)を指定します。
リスナー登録・解除
リスナー登録boolean ret = mng . registerListener (SensorEventListener listener,Sensor sensor,int samplingPeriodUs) ;
mng : センサー管理オブジェクト
sensor : センサー制御オブジェクト
listener : リスナーオブジェクト
samplingPeriodUs : 読み取り頻度
ret : 戻り値(true:成功)
リスナーオブジェクトには、SensorEventListenerインタフェースを実装している必要があります。
PROCESSINGで使うなら、Sketchクラスに上記インタフェースを実装するか、専用のクラスを定義するのが良いでしょう。
samplingPeriodUsには、SensorManagerクラスが持つ以下の何れかの定数を与えます。
定数 | 意味 |
---|---|
SENSOR_DELAY_NORMAL | 通常のレート。デフォルト値 |
SENSOR_DELAY_UI | 画面表示やUIを伴うレート |
SENSOR_DELAY_GAME | GAMEに適したレート |
SENSOR_DELAY_FASTEST | できるだけ早くデータを取得するレート |
データをすばやく引き取ろうとすればするほどバッテリーを食いますので、そこは注意してください。
バッテリー消費を最小限に抑え、できるだけゆっくりとイベントを通知すれば良いのであれば、samplingPeriodUsの後ろにmaxReportLatencyUs引数を追加する事が可能です。
maxReportLatencyUsには、イベントをアプリケーションに報告する前に遅延させる最大時間(マイクロ秒)を指定します。
maxReportLatencyUs引数がない場合(上記で紹介した例)は、maxReportLatencyUsに0を指定した場合と等価になります。
リスナー解除void mng . unregisterListener (SensorEventListener listener) ;
listener : リスナーオブジェクト
センサーの値を読み取る必要がなくなったら、速やかにリスナーを解除し、データの通知を停止します。
データの取得
データの取得void onSensorChanged( SensorEvent event) ;
event : センサーイベント
センサから新しい値を取得した場合に呼び出されるメソッドです。SendorEventについては 公式サイト を参照してください。
どのセンサーからのデータであるかは、SensorEventのsensorで知ることが可能です。
またSendorEventからデータ(values)を取得する際には、できるだけ速やかに取得する必要があります。
照度はルクス(lux)で、event.values[0]の値のみ利用します。他のセンサー(加速度センサーなど)では event.values[1]やevent.values[2]を使うものがありますが、照度センサーでは利用できないので注意してください。
【関連記事】
サンプルプログラム
シンプルな照度計測例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.content.Context; /** * PROCESSING 照度センサー Sample * @auther MSLABO * @version 2018/06 1.0 */ Activity act; SensorManager mSensor = null; SensorCatch sensorCatch = null; float[] lightValues = new float[1]; float fontSize; //センサー読み取りクラス class SensorCatch implements SensorEventListener{ //センサーの値が変化した時呼ばれるイベントメソッド public void onSensorChanged(SensorEvent sensorEvent) { switch(sensorEvent.sensor.getType()) { //照度センサー case Sensor.TYPE_LIGHT: lightValues = sensorEvent.values.clone(); break; } } public void onAccuracyChanged(Sensor sensor, int i) { //処理なし } } //画面表示直前イベント void onResume(){ super.onResume(); act = getActivity(); //センサー管理オブジェクト取得 mSensor = (SensorManager)act.getSystemService(Context.SENSOR_SERVICE); //センサーリスナーオブジェクト生成 sensorCatch = new SensorCatch(); //センサー制御オブジェクト取得 Sensor cSensor = mSensor.getDefaultSensor(Sensor.TYPE_LIGHT); //照度センサーのリスナー登録 if( cSensor != null ){ mSensor.registerListener(sensorCatch, cSensor, SensorManager.SENSOR_DELAY_UI); } } //画面非表示時イベント void onPause() { super.onPause(); //センサーからリスナーを削除する if(mSensor != null){ mSensor.unregisterListener(sensorCatch); } } void setup() { fullScreen(); //縦置き固定 orientation(PORTRAIT); //文字サイズ、文字位置指定 fontSize = 24 * displayDensity; textSize(fontSize); textAlign(LEFT, TOP); fill(255); } void draw() { background(0); //センサーの値を表示する String dispText = String.format( "照度\n %s\n", String.valueOf(lightValues[0])); text( dispText,0, 0, width, height); } |
シンプルな照度計測例です。検知した明るさを画面に表示します。検知する値の精度は、機種により差異がある可能性があります。
手持ちのスマホ(V580)では、照度センサーはかなり大雑把な値を拾うようでした。
照度に応じて電球を光らせる例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.content.Context; /** * PROCESSING 照度センサー Sample * @auther MSLABO * @version 2018/06 2.0 */ Activity act; SensorManager mSensor = null; SensorCatch sensorCatch = null; float[] lightValues = new float[1]; float fontSize; PImage lightImg; //センサー読み取りクラス class SensorCatch implements SensorEventListener{ //センサーの値が変化した時呼ばれるイベントメソッド public void onSensorChanged(SensorEvent sensorEvent) { switch(sensorEvent.sensor.getType()) { //照度センサー case Sensor.TYPE_LIGHT: lightValues = sensorEvent.values.clone(); break; } } public void onAccuracyChanged(Sensor sensor, int i) { //処理なし } } //画面表示直前イベント void onResume(){ super.onResume(); act = getActivity(); //センサー管理オブジェクト取得 mSensor = (SensorManager)act.getSystemService(Context.SENSOR_SERVICE); //センサーリスナーオブジェクト生成 sensorCatch = new SensorCatch(); //センサー制御オブジェクト取得 Sensor cSensor = mSensor.getDefaultSensor(Sensor.TYPE_LIGHT); //照度センサーのリスナー登録 if( cSensor != null ){ mSensor.registerListener(sensorCatch, cSensor, SensorManager.SENSOR_DELAY_UI); } } //画面非表示時イベント void onPause() { super.onPause(); //センサーからリスナーを削除する if(mSensor != null){ mSensor.unregisterListener(sensorCatch); } } float bx, by; void setup() { fullScreen(); //縦置き固定 orientation(PORTRAIT); //ランプ画像読み込み lightImg = loadImage("light.png"); //画面サイズに合わせて拡大縮小する bx = width/1080.0f; by = height/1920.0f; lightImg.resize( (int)(lightImg.width * bx), (int)(lightImg.height * by)); //文字サイズ、文字位置指定 fontSize = 32 * displayDensity; textSize(fontSize); textAlign(LEFT, TOP); fill(255); } void draw() { background(lightImg); //センサー値をもとにランプを光らせる //20000 lux = 曇天屋外 // 3000 lux = かなり明るい室内 float power = map( lightValues[0], 0, 20000, 160, 0 ); if( power > 200 ) { power = 200; } particle( 560*bx, 1050*by, power); //センサーの値を表示する String dispText = String.format( "照度 : %s", nf( lightValues[0], 5, 1)); float fx = (width - textWidth(dispText))/2; float fy = 1450 * by; text( dispText,fx, fy ); } //ランプを光らせる処理 //lx,ly : 光源の中心座標 lightPower:光の範囲 void particle(float lx, float ly, float lightPower){ loadPixels(); // パーティクルの影響範囲のピクセルについて、色の加算を行う for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int pixelIndex = x + y * width; // ピクセルから、赤・緑・青の要素を取りだす float r = pixels[pixelIndex] >> 16 & 0xFF; float g = pixels[pixelIndex] >> 8 & 0xFF; float b = pixels[pixelIndex] & 0xFF; // パーティクルの中心と、ピクセルとの距離を計算する float dx = lx - x; float dy = ly - y; float distance = sqrt(dx * dx + dy * dy); // 三平方の定理 // 0除算の回避 if(distance == 0){ distance = 0.01; } //光の強さを距離で割る(少し青白い光) r += (200.0 * lightPower) / distance; g += (200.0 * lightPower) / distance; b += (255.0 * lightPower) / distance; // ピクセルの色を変更する pixels[pixelIndex] = color(r, g, b); } } updatePixels(); } |
検出した照度に応じて、電球の絵が光ります。ルクスが高ければ暗く、低ければ明るくなります。
上記プログラムで電球を光らせる部分には、Proce55ing.walker,blog 様に掲載されていたロジックを改変して利用させて頂きました。
またルクスの目安については Microsoft 様のサイトを参照しました。ありがとうございます。
<出力サンプル>
(画像URL:photo-ac 様:dronepc55さん)
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。