I2C と SPI の違い

I2C と SPI の違い

I2C と SPI は、ほとんどおなじ

制御するデバイス
大抵は Raspberry Pi になるけど
こっちをマスターといって

制御される側の部品のことを
スレーブという

この呼び方の違いは
利用する部品がどちらに対応しているかにより異なる

仕組みは
どっちもクロック信号に載せてデータをやりとりする

このデータ交換は
I2c なら1本の信号線を使い

SPIなら2本の信号線を使う

これは I2C が双方向で
SPI が片方向となっているため

ちなみに
I2C のデータ線はSDA という

SPIの場合
マスターからスレーブ方向を MOSI
スレーブからマスターが MISO
という

そして、マスターとスレーブとの間で
MOSI 同士
MISO 同士
をつなげる

SPIの場合、かなりわかりにくく
自分からでていくのが
SDO とか Dout
といって

自分に入ってくるのを
SDI とか Din
と書いているときもある

こう書いてある場合
Out と In で接続する

例えば
マスターの SDO を
スレーブの SDI に接続する

I2C でも SPI でも
複数のスレーブとつなげることができるため
どれと通信するか識別する必要がある

I2C を使っている場合なら
スレーブ側にアドレスを持つので
マスターからアドレスを指定して制御する

SPIなら、専用の信号線があるので
各スレーブが持つ CS や SS を
マスターがロー、つまりGNDにして
デバイスを選択することになる

なお、マスターはスレーブの数だけ信号線が必要になる

Raspberry Pi に搭載されているのは
2本で CE とかかれている

Androidカメラの露出やオートフォーカスの制御

Androidカメラの露出やオートフォーカスの制御

カメラでオートフォーカスを使うには
autoFocus()
を使う

露出を変更したいのなら
setExposureCompensation()
を使う

これらをやる前に、まずは
カメラプレビューを実装しておくこと

次に、端末がオートフォーカス対応か判定する

public static boolean isSupportFocus(Context context){

return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS);

}

これで、結果がtrueなら使えるし
falseなら使えない

判定ができたらオートフォーカスを使うため
Camera.autoFocus()を使う

private void setCameraAutoFocus(){

if(mCamera !=null && mIsSupportFocus){

mCamera.autoFocus(new AutoFocusCallback(){

@Override
public void onAutoFocus(boolean success, Camera camera){
//AutoFocusのリセット
mCamera.cancelAutoFocus();

//AutoFocus開始
mCamera.autoFocus(null);
}
});
}
}

そして、露出情報の取得
露出を変更する前に
デバイスに搭載されているカメラの
露出情報を取得する

これで取得した値を元に
現在の露出値や
露出値の上限、下限を設定

//下限の設定
mMinExposure = mCamera.getParameters().getMinExposureCompensation();

//上限の設定
mMaxExposure = mCamera.getParameters().getMaxExposureCompensation();

//現在の露出値の算出
mExposureValue = mCamera.getParameters().getExposureCompensation();

そして、事前に取得した露出情報を露出の値を変更する

mExposureValue++;

//露出値の上限以上なら補正
if(mExposureValue > mMaxExposure){
mExposureValues = mMinExposure;
}

Parameters param = mCamera.getParameters();
param.getExposureCompensation(mExposureValue);

mCamera.setParameters(param);

mBtnExposure.setText(getString(R.string.exposure )+ mExposureValue);

次に、システム要件にオートフォーカスが必要なことを明記する
これで、非対応端末はインストールできる

これは、AndroidManifest.xmlへ追記する

<uses-feature android:name="android.hardware.camera.autofocus"/>

オートフォーカス連続使用の注意点として
デバイスによっては強制終了の可能性があるため
事前に
Camera.cancelAutoFocus()を呼び出し
オートフォーカスをリセットすれば安定する

あと、露出情報については
CameraParametersのメソッドをつかうことで
各露出情報を取得できる

getMaxExposureCompensation()
露出の上限

getMinExposureCompensation()
露出の下限

getExposureCompensation()
現在の露出値

Androidの写真をアルバムへ保存

Androidの写真をアルバムへ保存

写真をアルバムへ保存するには
Cameraクラスの
takePicture()を使って写真を撮影し
これで得たbyte配列のデータをJPGへ変換して保存する

まずは、カメラプレビューの実装し
次に、写真を撮影

写真の撮影にはtakePicture()を使う

mSurfaceView.setOnTouchListener(new OnTouchListener(){

@Override
public boolean onTouch(View v, MotionEvent event){

if(event.getAction() == MotionEvent.ACTION_DOWN){

if(mCamera !=null){

//前回撮影した画像を保存できない場合
if(!mIsSave){
mCamera.takePicture(null,null, mPictureCallback);
mIsSave = true;
}
}
}
return true;
}
});

次に、画像の保存先の設定

String savePath = Environment.getExternalStorageDirectory().getPath() + "SAVE_PATH";

File file = new File(saveFile);

if(!file.exists()){
file.mkdir();
}

Calendar cal = Calendar.getInstance();

SimpleDateFormat sdFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");

String imgPath = savePath +"/"+ "IMG_"+sdFormat.format(cal.getTime())+".jpg";

ちなみに、microSDに保存するときには
権限が必要になるため
AndroidManifest.xmlへ権限を追記する

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

次に、設定した保存バスを元に
FileOutputStream の
write()で画像データを書き込む
書き込み後はギャラリーへ反映するため
ContentProviderの更新をする

try{
FileOutputStream fos = new FileOutputStream(imgPath, true);

fos.write(data);
fos.close();

//ContentProvider update
ContentValues values = new ContentValues();
values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put("_data",imgPath);

getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

}catch(Exception e){

}

takePicture()には
3つのコールバック、つまり行う挙動を指定できる

Camera.ShutterCallback
キャプチャした瞬間、コールバックを返す

Camera.PictureCallback
撮影したrawデータの画像をコールバックとして返す

Camera.PictureCallback
撮影したjpegデータの画像をコールバックとして返す

Raspberry Pi のLED抵抗の値についてメモ

Raspberry Pi のLED抵抗の値についてメモ

LEDは
かける電圧をあげていくと
一定の電圧を超えたところで
それまで流れなかった電流が一気に流れる

このときの電圧のことを
順方向電圧
という

例として

高輝度5mm赤色LED 7cd60度 (10個入)
http://akizukidenshi.com/catalog/g/gI-01318/

の画面をみると

VF:2.1V
と書いてあり
これが順方向電圧を示している
VF がそれになる

おそらくこれが耐えれる電圧
これを超えないために抵抗をつける

また
標準電流:20mA
となっているので
この場合、通常は 20mA まで電流をおさえる
ということになる

ちなみに 1A は 1000mA

もし220Ωの抵抗をいれたなら
(電圧 3.3 V – LED の電圧 2.1V) / 220Ω
となるので
0.004 A
つまり
4mAとなる

最大値の 20mA まで電流をながすと
かなりまぶしいけど
Raspberry Pi には電流に制限があり
1つの GPIO に流せる電流量は
16mA
となっているので、これはできない

さらに Raspberry Pi では
ピンヘッダー全体で流せるのが
最大 50mA
しかないので注意

Ubuntu 14.04 LTS インストール

Ubuntu 14.04 LTS インストール

uvuntu 12.10のサポートが切れたため
サポート期間の長いLTSをクリーンインストール

あらかじめ必要なファイルはバックアップをとってあるので
あとは
インストール開始

デスクトップ以外に
12.04LTS のパソコンがあるので
そちらを元に作成

https://www.ubuntulinux.jp/News/ubuntu1404-ja-remix
のページから
ISOファイルをダウンロードする

今回は KDDI研究所のものをつかった
http://ftp.kddilabs.jp/Linux/packages/ubuntu-jp/release-cd/releases/14.04/ubuntu-ja-14.04-desktop-amd64.iso

これでISOファイルは入ったので
ISOファイルをDVDに焼く必要があるので
k3b を起動してISOファイルをDVDーRへ書き込み

インストールするときに、光学ドライブの調子がよくないみたいなので
最小限の項目のみでインストール

インストール完了後、いろいろとメモをとりたいので
Ubuntu ソフトウェアセンターで
tomboy をインストール

次に firefox が終了時にタブを閉じてしまうので
https://support.mozilla.org/ja/kb/restore-previous-session#w_akccaicdchagaeecageggo
を参考に
firefox を起動して
edit > preferemces
で設定画面をひらき
When Firefox stats の項目が
Show my home page
になっているので、これを
show my windows and tabs from last time
に変更する

Ubuntu Gnome 14.04 をインストールしてからやったこと

を元にインストールしていく

私の場合用途は仮想環境の構築と
Androd 関連の開発とかサーバーなどなど

あとは  windows 7 でゲームできればOK
というかんじ

なにはともあれ、まずはアップデートなので

sudo apt-get update
sudo apt-get upgrade

よく使うパッケージをインストールするので

sudo apt-get install git build-essential 

次に Chrome のインストール

sudo apt-get install chromium-browser chromium-browser-l10n

そしてドライバーを入れるため
http://www.howopensource.com/2012/10/install-nvidia-geforce-driver-in-ubuntu-12-10-12-04-using-ppa/
を参考に

sudo apt-add-repository ppa:xorg-edgers/ppa
sudo apt-add-repository ppa:ubuntu-x-swat/x-updates
sudo apt-get update
sudo apt-get install nvidia-current nvidia-settings

これでインストール

次に仮想環境のインストール

https://my.vmware.com/jp/web/vmware/free#desktop_end_user_computing/vmware_player/6_0

から
VMware Player for Linux 64 bit を選んでダウンロード

ダウンロードしたバージョンは 6.0.3

インストールに関しては
Linux(Ubuntu14.04等)にVMwarePlayerをインスコんごwwww

を参考に

cd ダウンロード/
sudo bash VMware-Player-6.0.3-1895310.x86_64.bundle 

でインストール

ちなみにアンインストール方法も調べてみた

LinuxのVMwarePlayer抹殺マン。

によれば

sudo vmware-installer -u vmware-player

でアンインストール可能

次に VMware Player のゲストマシンで 3Dアクセラレーションを有効化したいので
Linux ホスト VMWare 3D アクセラレーション

を参考に追加インストール

まず必要なパッケージをインストール

その前に端末がつかいにくいので、端末を起動して
編集 > キーボードショートカット
を選択して

メニューのアクセスキーを有効にする
のチェックを外します

次にパッケージのインストール

sudo apt-get install driconf 

あと vim を使いたいので

sudo apt-get install vim

GPIO 14 でLチカ

GPIO 14 でLチカ

GPIO 4 ではなぜかLチカできなくなったため
GPIO 14 で実験する

一般的なPCボードとか Ardunio などでは
スイッチがONであることを
3.3V か 5V で表す

ちなみに Raspberry Pi は 3.3 V
GPIO は 1 か 0 のデジタルで
1 が high
0 が low

とりあえずブレッドボードへ配線してみた

必要部品に関しては
ハック ラズベリーパイ Raspberry Pi 電子工作入門キット。
で揃うので、これを使用

配線は
GPIO 14
から
220Ωの抵抗につなぎ、これから
LEDの+、つまり足の長いほうへつなぐ

次にLEDのー
足の短いほうを
GNDにつなげる

GPIO 14 はGNDのとなりにある左上3番め

GNDは左上4番めになる

これで Raspberry PI を起動すると
LEDが点灯する

今回はこれを制御してみる
なお、LED操作には root 権限で動かすので

sudo su

で動かすことになる

まずは GPIO 14 を有効化する

echo "14" > /sys/class/gpio/export 

次にモードを出力モードに指定

echo "out" > /sys/class/gpio/gpio14/direction 

これで消灯しているので
LED点灯するには

echo "1" > /sys/class/gpio/gpio14/value 
echo "0" > /sys/class/gpio/gpio14/value 

で消灯になる

シェルスクリプトでも制御できるけど
実行する時には root 権限が必要なため
sudo をつけて実行する必要がある

今回、実験したけど

./led.sh: 3 行: echo: 書き込みエラー: デバイスもしくはリソースがビジー状態です

とエラーがでるもののLEDの点滅には成功

Androidカメラプレビューの利用

Androidカメラプレビューの利用

Androidのカメラプレビューを使うには
SurfaceViewを設置してプレビュー領域を作成

そして、Cameraクラスの
startPreview()を使ってプレビューを開始する

まずは、SurfaceViewの設置

これは、レイアウトファイルとなるXMLファイルへ追加する

<SurfaceView
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

次に、SurfaceHolderを取得して
コールバック処理を追加する

これは、設置した SurfaceView から
SurfaceHolder を取得し
SurfaceViewの生成時
変更時、破棄時のときの処理を追加する

コールバック処理は、そのときに行う処理のこと

まずは、SurfaceHolderの取得

mSurfaceHolder = mSurfaceView.getHolder();

次に、処理の追加
処理の追加は、addCallback()で追加できる
とりあえず、プロトタイプ

mSurfaceHolder.addCallback(

@Override
public void surfaceDestroyed(SurfaceHolder holder){
//破棄時の処理
}

@Override
public void surfaceCreated(SurfaceHolder holder){
//生成時の処理
}

@Override
public void surfaceChanged(SurfaceHolder holder int format, int width, int height){
//変更時の処理
}

)

これで、だいたいの骨格ができたので
次に、生成時の処理になる
surfaceCreated()の中身を書いていく

ここでは、カメラの初期化
そして、デバイスで利用できる
カメラプレビュー一覧も取得する

なお、取得失敗も考慮し、try catchで処理する

@Override
public void onCreated(SurfaceHolder holder){

//カメラの初期化処理
mCamera = Camera.open();
if(mCamera !=null){

try{
mCamera.setPreviewDisplay(mSurfaceHolder);

}catch(IOException e){
e.printStackTrace();
}

//利用可能なプレビューサイズの取得
mSupportedPreviewSize = mCamera.getParameters().getSupportedPreviewSizes();
}
}

次に、SurfaceChanged()の中身を書いていく
ここでは、ディスプレイサイズから
カメラプレビューに最適なサイズを選択し
カメラプレビューを開始する

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){

if(mCamera !=null){
Parameters param = mCamera.getParameters();

if(mSupportedPreviewSizes !=null){

//ディスプレイサイズに最適なサイズを設定
mPreviewSize = CameraUtil.getOptimalPreviewSize(mSupportedPreviewSizes, width, height);

//カメラのプレビューサイズをセット
param.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(param);
}

//プレビュー開始
mCamera.startPreview();
}

}

そして、
surfaceDestroyed()へ
SurfaceView破棄時のコールバック処理を書く
ここでは、カメラプレビューを使わなくなったときの処理を書く

@Override
public void surfaceDestroyed(SurfaceHolder holder){

//カメラの終了処理
mCamera.stopPreview();
mCamera.release();
mCamera =null;
}

あとは、カメラを扱うため
AndroidManifest.xmlへ権限を追記する

<uses-permission android:name="android.permission.CAMERA"/>

あと、surfaceHolder へ
addCallback()
で、コールバックを追加すると
以下のメソッドかわ追加される

surfaceDestroyed(SurfaceHolder holder)
これは、SurfaceViewが破棄されたときに呼ばれる

surfaceCreated(SurfaceHolder holder)
これは、SurfaceViewが生成されるときに呼ばれる

surfaceChanged(SurfaceHolder holderint format, int width, int height)
これは、SurfaceViewが変更されたときに呼ばれる

カメラプレビューでこれらのメソッドを使う場合

onSurfaceCreated()で
カメラを初期化

onSurfaceChanged()で
プレビューの設定と開始処理

onSurfaceDestroyed()で
カメラの終了処理をする

次に、カメラパラメーターの設定

カメラは、プレビューを開始する前に
カメラに様々なパラメーターを追加できる

プレビューサイズもパラメーターに該当するため
パラメーター扱いで変更できる

カメラパラメーターは
Camera.getParameters()で取得し
setParameters()でセットする

あと、Androidはデバイスごとに画面のプレビューサイズが代わるため
CameraUtilクラスの
getOptimalPreviewSize()を使う

Android近接センサーの値取得

Android近接センサーの値取得

近接センサーは、端末に物体が接近しているときの距離を検出するセンサー

これも、SensorManagerをつかえば値を取得できる

まず、SensorManagerのインストールを取得

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

次に、近接センサーの登録
これも
SensorManagerクラスの
registerListener()で登録

private void registerSensor(){

if(mSensorManager ==null){

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

mSensorManager.registerListener(
this, 
mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
mSensorManager.SENSOR_DELAY_UI
);
}
}

次に、バッテリー消費対策のため
センサー解除の実装

SensorManagerクラスの
unregisterListener()により解除可能になる

private void unregisterSensor(){

if(mSensorManager !=null){

mSensorManager.unregisterListener(this);
mSensorManager = null;

}
mvibrator.cancel();
}

これで登録と解除はできるので
近接センサーの値取得

近接センサーの値は、
onServiceChanged()
のeventオブジェクトに格納されているので
そこから取得する

@Override
public void onSensorChanged(SensorEvent event){

//近接センサーの値を取得
if(event.sensor.getType() == Sensor.TYPE_PROXIMITY){

if(event.values[0] ==0){
mVibrator.vibrate(new long[] {
100, 100
}, 0);

}else{

mVibrator.cancel();
}
}
}

今回は、近接センサーの値が 0 なら
バイブで振動するようにしている

Android気圧センサーの値取得

Android気圧センサーの値取得

Androidデバイスに気圧センサーが内蔵されているなら
SensorManagerを使い
気圧値の取得ができる

まずは、SensorManagerのインスタンス取得

Context.getSystemService()

SensorManagerのインスタンス取得する

mSensorManager= (SensorManager)getSystemService(SENSOR_SERVICE);

次に、気圧センサーの登録
これは、SensorManagerクラスの
registerListener()
を使い、気圧センサーを登録する

@Override
protected void onResume(){

super.onResume();

//センサー登録
mSensorManager.registerListener(
this,
mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE),
SensorManager.SENSOR_DELAY_UI
);

}

これだけだと、バッテリー消費が激しくなるので
解除方法も記述

解除は
SensorManagerクラスの
unregisterListener()をつかうことで
気圧センサーの登録を解除できる

@Override
protected void onPause(){

super.onPause();

//センサー登録解除
mSensorManager.unregisterListener(this);
}

これで、登録と解除はokなので、
次は、センサーから気圧値を取得し表示する

@Override
public void onSensorChanged(SensorEvent event){

//気圧値の取得
float[] values = event.values;
mTvPressure.setText(" "+values[0]);
}

とりあえず、これで取得や表示などはできるけど
気圧センサーは搭載されてない端末が多いため
搭載されてないことを前提に実装すること

方法は2つ
Google Play へ公開するのなら
uses-featureの設定により、Google Playからのインストールができないようにできる

もう一つは、プログラムでチェックする方法
こっちは、サードパーティーアプリとか
自作で入れるときに使う

まずは、uses-featureの場合
この場合、AndroidManifest.xmlへ設定する

<uses-feature
android:name="android.hardware.sensor.pressure"
android:require="true"/>

を追加する

次に、プログラムでの判定

public static boolean hasFeaturePressure(Context context){

return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_BAROMETER);
}

Androidデバイスの方位を調べる

Androidデバイスの方位を調べる

デバイスの方位を調べるには
加速度センサー、磁気センサーを使い
包囲角度を計算する

そして、東西南北どの方位に該当するかを計算し、方位を求めることになる

まず、SensorManagerのインスタンス取得

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

次に、センサーの登録
これは、
SensorManagerクラスの
registerListener()を使い加速度センサー、および磁気センサーを登録する

@Override
protected void onResume(){

super.onResume();

mSensorManager.registerListener(
this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);

mSensorManager.registerListener(
this,
SensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_UI);

}

次に、センサーの解除について

これは、SensorManagerの
unregisterListener()で
加速度センサー、および磁気センサーを解除する

@Override
public void onPause(){

//センサー解除
mSensorManager.unregisterListener(this);
super.onPause();
}

次に、方位角度を求める

これは、センサーで取得した値から方位角度を計算することで求められる

@Override
public void onSensorChanged(SensorEvent event){

switch(event.sensor.getType()){

//加速度センサーの値取得
case Sensor.TYPE_ACCELEROMETER:
mAcMatrix = event.values.clone();
break;

//磁気センサーの値取得
case Sensor.TYPE_MAGNETIC_FIELD:
mMgMatrix = event.values.clone();
break;
}

if(mMgMatrix !=null && mAcMatrix !=null){

float[] orientation = new float[3];
float R[] = new float[16];
float I[] = new float[16];

//加速度センサー、磁気センサーの値を元に、回転行列を計算する
SensorManager.getRotationMatrix(R, I, mAcMatrix, mMgMatrix);

//デバイスの向きに応じて回転行列を計算する
SensorManager.getOrientation(R, orientation);

//ラジアンから角度へ変換
float angle = (float)Math.floor(Math.toDegrees(orientation[0]));

//角度の範囲を0~360度へ調整
if(angle >=0){

orientation[0]=angle;
}else if(angle < 0){
orientation[0]= 360 + angle;
}

//得られた角度を画面へ表示
mTVAzimuth.setText(String.valueOf(orientation[0]));
}

このように、Androidでは
方位角を求めるには、
加速度センサーと磁気センサーを併用し
加速度の値
磁気センサーの値
デバイスの向きを元に回転行列を作成する

その後で、該当するラジアン値を角度へ変換することで
方位角を求めることができる

注意点として、センサーは一度登録すると解除するまで作動するため
バッテリー消費が激しくなる

このため、登録にはonPause()
解除には onResume()で行うようにする