shogouki's 技術系メモ

WindowsPhoneとか、WPFとか、Androidとかその辺の技術メモ

i-stopキャンセラーをArduinoで自作する

i-stop(アイドリングストップ)とは

アイドリングストップは、その名の通り、不要なアイドリングを止めることにより燃料消費を抑えるためのものですが、私の車(アテンザ)に搭載されているディーゼルエンジンは再始動時のバッテリー負荷が高く、バッテリーの寿命が短くなると考えられます。

そうすると、せっかく燃料消費を抑えてもバッテリー交換が早まってしまえば、環境負荷的にも金銭的にもあまり意味がなくなってしまいます。

i-stopをオフにするには

i-stopは、エンジン始動後にスイッチクラスターのi-stopボタンを長押しする事で有効/無効を切り替えることができますが、状態が記憶されないため、車に乗ってエンジンを掛けるたびにボタンを長押しして無効に切り替える必要があります。

それでは面倒なのでエンジン始動後、自動的にi-stopボタンを長押ししたことにして無効にするのが、"i-stopキャンセラー"です。

i-stopキャンセラーは4000円~で市販もされていますが、たかが数秒後にボタン長押しをエミュレートする装置に4000円も出すのはなんかもったいないです。

ネットではボンネットスイッチを外す方法も紹介されていますが、この方法は完全に無効化してしまうため、ボタンで再度有効にすることができないのと、イモビアラームも無効になってしまうらしいという問題があります。

i-stopボタンの解析

次に、i-stopボタンの仕組みを解析します。

ボタンの代わりなんだから、フォトカプラでアイソレーションしてスイッチさせれば…と思いますが、調べてみると、どうやらスイッチクラスターのスイッチによって抵抗値を変えることでハーネスの本数を減らす仕組みになっているようです。

i-stop OFF SWITCH INSPECTION

i-stopボタンは約85Ωにする必要がありますが、手元には330Ωの抵抗しかありません。しかし、4本並列にすることでなんと82.5Ωになります!

念のためブレッドボードに4本並列にしたものを、上記マニュアルを参考に、キーON後コネクタのB-Cにつなぐと、「ピピッ」と音がしたので大丈夫そうです。

Arduinoを使う

Arduinoは安価なマイコンで、プログラミングも簡単にできるため、ちょっとしたものを作るときにはよく使われます。

正規品のArduino Unoは3000円ほどしますが、クローン品は500円程度で入手可能です。さらに、今回はデジタルI/Oが一つあればいいので、より小さくシンプルな DigiSpark のクローン品を使います。

DigiSparkはさらに安く、Amazonでも300円程度です。 ここまでくると、555タイマーでディレイ・ワンショットを組むよりも、プログラムで解決した方が安く済みそうです。

また、DigiSparkはレギュレータも搭載しているため、直接12Vで駆動できるのもメリットです。

Arduinoのプログラミング

Arduinoの電源ON後、10秒経過後に2秒間出力をONにするプログラムを書きます。

1番の出力はボード上のLEDに接続されているため、これを動作確認用にして、フォトカプラは0番の出力で制御することにします。

動作後は無限ループに入って終了ですが、スリープモードにした方がいいのかもしれません。(そこまで消費電力は気にしなくてよいと思いますが…)

DigiSparkにプログラムを書き込むにはArduinoIDEにボード情報を追加したり、ドライバーをインストールする必要があり、また書き込みボタン押下後の特定のタイミングでUSBポートに差し込む必要がありますが、ネット上で既にたくさん紹介されているので割愛します。

プログラムが書き込めたら、USBポートに差しなおして10秒後に2秒間LEDが光ることを確認します。

電子工作

コントローラーは出来たので、次に、ユニバーサル基板にフォトカプラ、抵抗、DigiSparkを半田付けします。

車に組み込み

プラスはリアモニター用?に配線してあったものを利用。

f:id:shogouki3:20190126024232j:plain

マイナスはハンドル下にあったターミナルに接続

f:id:shogouki3:20190126024607j:plain

あとはスイッチクラスターの配線をエレクトロタップで分岐してそれぞれArduinoに接続して完成!

しばらく使ってみましたが、問題なく動作してます。

NuGetでインストールしたPrismはApplicationBarButtonCommandが機能しない

タイトルの通りです。

現在、NuGetでは「Prism Interactivity library for Windows Phone 7」が公開されており、
インストールできますが、これを使うとApplicationBarButtonCommandがうまく機能しないようです。

詳細(修正済み)
http://compositewpf.codeplex.com/workitem/8500

こちらから最新版がダウンロードできますので、こちらを素直に使うのが良さそうです。

しかし、ApplicationBarButtonCommandのターゲットの指定がボタンのTextってどーよ…
ローカライズするときは、両方ローカライズしてやればいいのかな…。
せめてNameで指定できるようになればいいのに。

ボタンを配列で扱いたい

ボタンを配列で扱いたい!という話が#wp7dev_jpで出ていたので、ちょっと試してみた。

<toolkit:WrapPanel Name="wrapPanel1">
	<Button Content="Button"
			Height="71"
			Name="button1"
			Width="160"
			Click="button_Click" />
	<Button Content="Button"
			Height="71"
			Name="button2"
			Width="160"
			Click="button_Click" />

	<!-- あとButtonとかその他UIコンポーネント好きなだけ -->
</toolkit:WrapPanel>

みたいなWrapPanelがあったとして、そこからButtonを引っ張り出して配列で扱いたい!
という場合。

…Buttonを配列で扱う良い例が思いつかないので、例えばボタンを押したらある一連のボタンのラベルがindex番号にしたい場合、

private void button_Click(object sender, RoutedEventArgs e)
{
	// WrapPanelの中にある要素からButtonだけ抜き出す
	var buttons = wrapPanel1.Children.Where(x => 
	{
		return x is Button;
	});

	// foreachでContent書き換え
	int count = 0;
	foreach (Button item in buttons)
	{
		item.Content = count++;
	}
}

というようにできます。
まず、wrapPanel1内にある子要素から、Buttonだけ抽出し、それをforeachでループさせてます。

それだけといえば、それだけですが…。

WrapPanel内の特定のボタンを引っ張り出したい場合は、そのボタンに特定のプリフィックスつけて、名前でフィルタリングしてやればいいでしょう。

上記処理を実際に組み込むと、

ボタンをタップすると
f:id:shogouki3:20120428132824p:plain

ラベルが変わる
f:id:shogouki3:20120428132825p:plain

ようになります

/* こういう意味じゃなかった…? */

画像をローカライズするテスト

AppHubに「画像のローカライズしたいんだけど」って質問があったので、方法を考えてみました。

アプリを国際化する

まずは、先人のノウハウをもとにアプリを国際化します。

参考 覚え書き:Windows Phoneアプリをローカライズする
http://darutk-oboegaki.blogspot.jp/2011/06/windows-phone_10.html

画像をローカライズする

言語環境ごとに画像をローカライズする方法を考えます。

  1. 言語リソースに画像を追加する
  2. ImageのUriを言語リソースに登録し、それをBindingで参照する
  3. 言語設定を取得し、コードビハインドからImageのSourceにセットする

1は試してみたところ、System.Drawing.Bitmap名前空間がWPでは使えないため×っぽいです。
3は確実に出来るのでいいのですが、画像ごとに処理を記述するのは面倒です。

というわけで、今回は2を試してみます。

まず、こんな感じでローカライズする画像を追加します。
f:id:shogouki3:20120319223750p:plain

次に、言語リソースに文字列として、
名前:TopImageUri
値:Images/other/other.png

というように、対応言語ごとに表示したい画像のUriを同じ名前で登録します。

ローカライズしたい画像はこのようにXAMLで記述します。

<Image Source="{Binding Source={StaticResource AppResourcesProvider}, Path=AppResources.TopImageUri}"/>

そうすると、

英語環境
f:id:shogouki3:20120319232402p:plain

日本語(英語以外)環境
f:id:shogouki3:20120319232458p:plain

と、このように画像が環境によって変わりました。




が、デザイン画面ではこのように画像が表示されません。
f:id:shogouki3:20120319233504p:plain

これは困るのでちょっと良い方法がないか検討してみます&あったら教えてください。

NUnitを使ってみるテスト

WindowsPhoneはおろか、今までxUnitは使用したことがありませんでしたが、

開発効率を上げるためにも自動テスト(ユニットテスト?)とやらを勉強しようと思い、

ひとまずWindowsPhoneでNUnitを使ってみました。

環境

NUnitを使用するためには

  • WP開発環境
  • NuGet

がインストールされている必要があります。

使い方

テスト対象の用意

まずは、適当にテスト対象のアプリを作ります。
f:id:shogouki3:20120318000854p:plain

で、適当にテスト対象のクラスを作ります。

public class Tax
{
    /// <summary>
    /// 消費税込の価格を返す
    /// </summary>
    /// <param name="price">税抜き価格</param>
    /// <returns>税込み価格</returns>
    public int AddExciseTax(int price)
    {
        return (int)(price * 1.05);
    }
}

テストアプリを追加

次に、テスト用のアプリプロジェクトを追加します。
f:id:shogouki3:20120318004929p:plain

このプロジェクトに対して、NuGetでNUnitをインストールします。
f:id:shogouki3:20120318001846p:plain

・・・何をどうしていいか分からないので、まずはNUnit_Readme.txtを読んでみます。
Readmeによると、

UnitTest.cs と Test_Readme.txt はいらないから消していいよ。
例は NUnitTest.cs に書いてあるから。

あと、MainPageのLoadedイベントのハンドラに
this.StartTestRunner(new Microsoft.Silverlight.Testing.UnitTesting.Metadata.NUnit.NUnitProvider());
って書いとけばおk

とのことなので、要らないファイルを消して、MainPageのLoadedに上記コードを記述します。

namespace TestApp
{
    public partial class MainPage : PhoneApplicationPage
    {
        // コンストラクター
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            this.StartTestRunner(new Microsoft.Silverlight.Testing.UnitTesting.Metadata.NUnit.NUnitProvider());
        }
    }
}

次に、Tax.cs のテストをするために、TestAppプロジェクトの参照にTargetAppを追加します。
f:id:shogouki3:20120318002548p:plain

そんで、NUnitTest.csのテストコードを書き換えます。

namespace TestApp
{
    [TestFixture]
    public class NUnitTest
    {
        [Test]
        public void TaxTestPass()
        {
            Assert.AreEqual(105, TargetApp.Tax.AddExciseTax(100));
        }
        [Test]
        public void TaxTestFail()
        {
            Assert.AreEqual(100, TargetApp.Tax.AddExciseTax(100));
        }
    }
}

この例では、100円の税込み価格が105円になるかチェックするテスト(成功例)と、
価格が変わらないテスト(失敗例)を記述しています。

実行してみる

のまえに、TestAppプロジェクトを右クリックして、スタートアップ プロジェクトに設定しておきます。

それが出来たらアプリを起動してみます。
起動すると、こんな画面が出てきて…
f:id:shogouki3:20120318004052p:plain

しばらくするとテストが実行されます。
f:id:shogouki3:20120318004445p:plain

右下を見てみると、「1Pass」「1Fail」と書いてあり、期待通りの結果になっているようです。
もう少し詳しく見てみると
f:id:shogouki3:20120318004525p:plain

このように、どのテストが失敗したか分かります。

まぁ、xUnitの実装方法についてはど素人なので、おかしなことやってるかもしれませんが…。
とにかく、これでテストするところまで出来ました。

あとは、リファクタリングするなり、なんなり。