.NETラボ勉強会 Windows IoT on RPi2 のビデオ

.NETラボ勉強会 で Windows 10 IoT Core on Raspberry Pi のハンズオンをやりました | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7212

のビデオを撮ってもらったので、youtube にアップしました。固定カメラだったので、実験中のビデオをはほぼほぼカットして今って、雰囲気だけ5分に縮めてあります。手前のノートブックに Visual Studio 2015RC が入っていて、Raspberry Pi にアップロード。そのまま LED がチカチカするか、VGA のモニタに時刻が写るか、という具合です。ブレッドボードにタクトスイッチを付けて、モニタに表示させよう、というところが失敗していますが、このあたりはいずれ家で試してみて撮り直しをしましょう。

実況ビデオ

 

 

サンプルコード

winiot_rpi2_src.zip

カテゴリー: RaspberryPi, Win IoT | .NETラボ勉強会 Windows IoT on RPi2 のビデオ はコメントを受け付けていません

『絶歌』を否定する

あまり、批判的な記事を書くことはなくなったのだが、これだけは残しておこう。書いた後に、エディタから消し去ってしまうか、そのままブログに載せるかは別のこと。自分が何をしようとしているのかを対象化することに「意義」があること、の実践でもある。

当然のように批判を浴びている元少年Aによる「絶歌」である。元少年Aというと、如何にも更生したかのような(正確に言えば正常に戻ったかのような)言い方になってしまうが、酒鬼薔薇事件の犯人であり、そこには「元」という冠詞はつかない。むろん、「刑期」を終えたのだから、刑期の前のように人権は扱われるべきではあるものの、直接的な被害者、殺人という事件の特異性、今回の「手記」を出してしまうような状況を鑑みれば、被害者の視点から「反省していて受け入れられる人」となるかは疑問である。

酒鬼薔薇事件が起きた当時「なぜ、人を殺してはいけないのか?」という問いが「流行し」その後に少年法が改正されたはずだ。一度、少年法として緩くなったところに起こった事件で、その後、少年法が強化された。そんなきっかけとなる事件でもあり、ある「緩い少年法」のもとに裁かれたという事実がある。それを考えると、当時、犯人と思われる少年(容疑者という範疇だった)が保護されるものであり、また「幼年時期に起こした過ちは、なんらかの家庭環境にて影響があるものだから、本人だけに責任があるわけではない。それゆえに更生したのちは、社会に受け入れられるべきであり、そのためにも名前を公表するべきではない」という根拠から、常に名前は伏せられていた。今となっては、インターネットで手軽に検索できるものでもあるし、何処の誰だか特定も可能であろう。

「なぜ、人を殺してはいけないのか」の議論に関しては、色々な「有識者」がテレビ等で議論したにも関わらず結論はでなかった。非常に馬鹿馬鹿しい話ではないけれども結論がでなかった。憲法や刑法などで人を殺してはいけないという意見もあれば、人を殺したことにより自分が苦しむからとか、人を殺すこと自体が絶対駄目だとか、じゃあ戦争の場合はどうなんだとか、そういう些末な議論が多かった。それが「些末」に感じたのは、そこには被害者が不在だったからだ。直接的な被害者は「殺されてしまった」ので生きることができないし、残された遺族(あるいは子を殺された親)にとっては、殺人者を肯定できる意義は全くない。そこには、犯罪者への同意は存在しない。時にして、そういう自己卑下になることもあるが(自分の父親が交通事故にあったときがそれだ)、いや考えてみれば、他人と自分とは「違う」ということ、相手にとっては自分は他者であることを強く意識すれば同調なんてする必要はない。被害者にとっては、子を殺された厳然たる「被害者」という立場があり、相手はどうやっても「犯罪者」だ。
そういう、曖昧模糊とした他人行儀な議論を続けた末に『「少年A」この子を生んで』というものが出版されて、マスコミ等で話題になる。いまでこそ Amazon 等で批判を受けた批評が残っているが、当時の盛り上がりは、実に被害者が不在であることと示し、そこには「酒鬼薔薇事件」を消費する世間という姿があった。いや、当時はそうは思ってはいなくて、単に嫌な感じがしていただけなのだが、今の私だと解る。それは「事件」そのものを消費している世間(主にテレビ?)があり、その突飛な事件こと話題性こそがターゲットを被害者ではなく「犯罪者」のほうに目を向けさせて、厳然と存在する事実であった「殺人」さえもおぼろげにさせてしまっている。そこには、一般的な「反省」の姿や、思いやりの姿がない。

殺人者であり死刑囚であった永山則夫は獄中で小説を書き、獄中で結婚をし、獄中のまま死刑になった。殺人者にとって「小説」を書く自由があるのかどうか、また、書いた小説が賞を取るという「栄誉」を与えられてしまうのかどうか、という問題が当時はあった。勿論、永山則夫は成人男性であり、少年Aは未成年であり少年法の範疇である、という違いはあるかもしれないが、「絶歌」なる手記を出版してしまうという時期には32才という成人でもあり、そこにはなんらかの分別が必要であろう。それは、この文章自体のように書き連ねることの自由はあるが、その自由を「被害者」にまで行使して良いのか?という「分別」である。
永山則夫の小説は買ったことがある。が、『「少年A」この子を生んで』も『絶歌』も買わないだろう読まないだろう。なんらかの折りに資料として手を取るかもしれないが、その時は被害者の出した手記と同時に読むだろう。なぜだろう。そこには「殺人事件」を商品化する過程があり、それを消費する自分が見えてくるからだ。確かに、言論は自由ではあるし、ともあれば誰かを批判するための出版は自由であろう。それ揺れに、読まない自由もあるし買わない自由もある。が、この出版に関しては「言論の自由」以外のところにある厭らしさ、というか絶対的な否定感がある。おそらく、それが「殺人」に対して自己商品化する態度だ。

話を元に戻そう。「なぜ、人を殺してはいけないのか」の議論がなされ、結局のところ「なぜ、人を殺してはいけないのか」の結論が出なかった時代であった訳だが、自分の中では確信になるものがある。「人を殺すような人がはびこる世間は、自分にとって危険だ」からだ。おそらく、利己的な遺伝子と自分が属する社会の在り方(分化する社会)からの結論になる。人を殺すことの不利益もそうだから、人を殺す人が蔓延る世の中も私にとって不利益である。この論法から言えば、「酒鬼薔薇事件」を起こした犯人が「絶歌」という本を出版し、被害者の意見も聞かず自己満足のために出版へと踏切り世の中になんらかの「名」を成すというスタイル(まさしく、このスタイルそのものが「酒鬼薔薇事件」だということがわかる)を、私は肯定することはできない。かつ、私はそういうものを受け入れる社会は否定する。

カテゴリー: 雑談 | 3件のコメント

DataTable よりも List を使うと 10 倍早くなる(続編)

うちのサイトでは地味にアクセス数が多いページで、

意外と遅い DataTable 、なので List を使うと 5 倍早くなる | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2228/comment-page-1#comment-24840175301577487

というのがあります。もう4年程前の記事で、業務で VB.NET 2.0 を使わないといけなくて、LINQ が使えなかったので、DataTable にしようか、List にしようかという調査の記録です。今だと、もうちょっと色々なやり方があるのですが、ちょっとコメントとが付いたので、計測しなおしてみました。

■list を使う
■list に構造体を使う

上記2つのケースについて、他の3つのケースにて行っている
「あえて最初に行を追加しておく」処理の記述がないように
お見受けしますが、これは単なる誤記であり実際は記述の上
実行された計測結果ということでしょうか。

随分前だったので覚えていないのですが、おそらく比較コードのミスです。あらかじめ1万行いれておいたのは「更新系」をチェックする必要があったので、このままだと list のほうが有利に働きますよね。
気になったので Go4 と Go5 だけ抜き出して書き直しました。

実験コード

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    DataTable MakeDataTable()
    {
        var dt = new DataTable();
        for (int i = 0; i < 100; i++)
        {
            dt.Columns.Add(new DataColumn(&quot;x&quot; + i.ToString(), typeof(string)));
        }
        return dt;
    }

    double Go4()
    {
        var dt = MakeDataTable();//  new DataTable();
        // データ更新チェックのため、あらかじめ1万行を作る
        for (int i = 0; i < 10000; i++)
        {
            var row = dt.NewRow();
            dt.Rows.Add(row);
        }
        // 計測開始
        var start = DateTime.Now;
        var tm = DateTime.Now;
        for (int i = 0; i < dt.Rows.Count; i++)
        {
            var row = dt.Rows[i];
            for (int j = 0; j < 100; j++)
            {
                row[j] = tm.ToShortDateString(); tm.AddSeconds(1);
            }
        }
        var tend = DateTime.Now;
        var span = (tend - start).TotalSeconds;
        return span;
    }

    double Go5()
    {
        var dt = new List<object>();
        // データ更新チェックのため、あらかじめ1万行を作る
        for (int i = 0; i < 10000; i++)
        {
            var row = new object[100];
            dt.Add(row);
        }
        // 計測開始
        var start = DateTime.Now;
        var tm = DateTime.Now;
        for (int i = 0; i < dt.Count; i++)
        {
            var row = dt[i] as object[];
            for (int j = 0; j < 100; j++)
            {
                row[j] = tm.ToShortDateString(); tm.AddSeconds(1);
            }
        }
        var tend = DateTime.Now;
        var span = (tend - start).TotalSeconds;
        return span;
    }

    /// <summary>
    /// DataTable を利用
    /// </summary>
    /// <param name=&quot;sender&quot;></param>
    /// <param name=&quot;e&quot;></param>
    private void button1_Click(object sender, EventArgs e)
    {
        var t = this.Go4();
        label1.Text = t.ToString();
    }

    /// <summary>
    /// list を利用
    /// </summary>
    /// <param name=&quot;sender&quot;></param>
    /// <param name=&quot;e&quot;></param>
    private void button2_Click(object sender, EventArgs e)
    {
        var t = this.Go5();
        label1.Text = t.ToString();
    }
}

どちらも、1万行いれておいて、その行に対して逐一データを修正するというスタイルになっています。
コードは Visual Studio 2013 を使っています。

実験結果

DataTable 8.32, 8.12, 8.12 sec
list 0.88, 0.83, 0.83 sec

とう具合に、DataTable よりも list のほうが 10 倍早くなっています。
すべてオンメモリで動くために、後は CPU 速度の違いになるとは思うのですが、PC が異なるし(こっちの PC のほうが断然早いので)相対的な結果になります。以前の 4,5倍よりも開きが大きくなっています。絶対値の違いは、VB が C# よりも遅いのではなくて、計測する CPU の差です。

カテゴリー: 開発 | DataTable よりも List を使うと 10 倍早くなる(続編) はコメントを受け付けていません

.NETラボ勉強会 で Windows 10 IoT Core on Raspberry Pi のハンズオンをやりました

思い付きでやったので、準備不足…な感は否めませんが、ひとまずハンズオンっぽいものをやりました。.NETラボ勉強会の面子にビデオを撮ってもらったので、後で編集して Youtube 等に上げたいと思います。初動が悪かったので、Raspberry Pi の初心者ばかりが集まる=手ぶらでやってくる方がほとんどと思ったのですが、予想に反して Raspberry Pi 2 を持って来て下さった方が8名余りもいらっしゃいました。ありがとうございます。ちょっと、そっちのほうは予想しなかったので、ほぼ放置状態になってしまったのが申し訳ない/残念なのですが、ひとまず、Raspberry Pi 2 に Windows IoT Core を入れるのは結構大変である、ってのは実感して頂けたと思います。いやいや、あれだけ色々な人がやって、結構躓くのだから、家でひとりでやると2時間ぐらいうんうん唸ってしてまっても仕方がないのですよ。情報的には、英語のサイト Windows IoT – Get Started から辿れるところにありますが、各自の環境(Mac, Windows, 仮想環境, micro SD カードの状況などなど)が異なっていて、一筋縄にはいきません。挙句、終わりの5時頃にできたのは、LED をチカチカさせるところまでです。

image

ただし、その前に「どこで躓くのか?」を話したのと、後ろのほうで「何ができるのか?」を話したので、Hello world 的に Lチカを済ませた後に「どこに進めばよいのか」は何となく掴めたのではないか、とは思っています。Raspberry Pi 2 自体は 5,000円程度はするし、時間とお金を掛けるのであれば、それなりに「何か」を掴み取りたいものです。

土曜日には、あえて言いませんでしたが、Windows IoT Core on RPi で四苦八苦するよりは、Raspberry Pi にすんなり Rasbian を乗せて制御したほうが断然いいです。また、Linux に四苦八苦するよりは、Arduino を使ってさっくりとセンサー制御を学んだほうがいいです。そのあたりは、適材適所なところと、「将来的に何をするのか?」の目標を踏み間違えないようにすれば、無駄な学習をせずにすみます(勿論、あえて「無駄な学習」をして異なる知見を得る方法もあります。私もよくやりますが)。

Windows Iot Core on Raspberry Pi の準備

来週から一週間ほど 6/1(月)Windows 10 for Raspberry PI2 開発実習ハンズオンセミナー – connpass が開催されるので、躓きそうなところ(勝手に)支援しておきます。18:00 スタート 21:00 終了というハードスケジュール(平日なので夕方から開催。たぶん de:code に合わせたと思うのですが、かなりきついスケジュール)なので、どこかで躓くと、Lチカもできずに帰ることになってしまうので。

  • 開発環境が Windows 10 IP + Visual Studio 2015 RC となっていますが、現在の Windows 10 IP の build が非常に不安定なので、Windows 8.1 + VS2015RC の組み合わせも検討してみてください。.NET ラボのハンズオンでは半分ぐらいが Mac に Windows 10 IP を入れていましたが、最新のとそうでないものとが混在すると結構ややこしいです。
  • 古い Windows 10 IP だと micro SD カードに書き込む dism のコマンドが異なっています。矛盾していますが、Windows 10 IP は新しいバージョンを使って micro SD カードに焼きこみます。ある意味、micro SD カードへの焼きこみだけ Windows 10 IP で行って、開発環境は Windows 8.1 + VS2015RC という組み合わせが安定します。ちなみに、Visual Studio 2013 と 2015RC は混在できています(これは安定しています)。
  • Windows 10 IP + Visual Studio 2015RC の組み合わせの場合、VS2015RC を立ち上げるときには「開発者モード」にする必要があります。そうしないと、Windows IoT の XAML デザイナが動きません(Windows 8.1 の場合は、もともと動かないので、気にする必要はありません)。デザイナを有効にするためには、「設定」→「Update & security」→「for Developers」を開いて設定します。ちょっと前の Windows 10 IP では、このページが落ちてしまうので Windows 10 を入れた直後に VS2015RC を入れて起動するとXAMLデザイナが動かないときの対処方法 | Moonmile Solutions Blog を参考にして、グループポリシーを設定してください。
  • micro SD カードに焼きこむときに dism コマンドを使いますが、その中の「/ImageFile:flash.ffu」にある、flash.ffu というのは焼きこむファイル名です。Windows_IoT_Core_RPI2_BUILD.zip を解凍すると flash.ffu というファイルがあるので、カレントディレクトリを cd コマンドで移動させてください。コマンドプロンプトを管理者モードで開くと「c:¥¥windows¥¥system32」になります。そのまま dism コマンドをコピペすると flash.ffu が見つからないので書き込みエラーになります(意外と嵌る人が多い)。
  • 多分、Win IoT のプログラムはユニバーサルアプリで作ると思うのですが、あらかじめ Visual Studio 2015 RC を立ち上げて、Universal アプリのテンプレートをダウンロードしておきます。
    以下な、感じになっていれば ok です。回線が細いと、このアップデートだけで20分以上かかります。最初の Universal アプリを作ったときテンプレートがダウンロードされるので、適当なプロジェクトで1回だけ作成しておきます。

image

  • できれば、Raspberry Pi 2 に Windows 10 IoT Core を乗せて Hello world するまで | Moonmile Solutions Blog 等を読んで、micro SD カードを Raspberry Pi に挿して Windows IoT Core の起動だけは確認しておくと安心です。ここまでを含めてハンズオンかもしれませんが、ここをやっておくだけで MS 太田さんの手間がかなり省けます。
  • 電源キットは、2A 5V の…となっていますが、実はノートパソコンから USB 給電だけでも起動します。ただし、HDMI 接続をしているときはうまく立ち上がらないことが多い(起動はしているので、Powershell やアップロードなどは問題ない)ので、電源アダプタは持っておくといかもしれませんね。Android 用の急速充電器(2A版)があればそれを使うとよいです。おそらく、iPad の充電器も使えるのではないかと。まあ、行った先で若松通商さんで買うのもアリですね。

あとは、受講用に Azure を登録しておくとか諸々ありますが、それらは別途、connpass のドキュメントを見てください。

ハンズオンの実際

少しぐらいは資料を用意すればよかったのと、事前に作っておいた(実は、超音波センサーのプログラムは前日…というか当日の夜に作ってあった)プログラムは、配布すればよかったですね。なんとなく、Raspberry Pi を各自動かしてみてから、の頭があったので配布し忘れていたのですが。

image

組み込みに関しては、全くやったことはないけど興味がある方から、自前で液晶ディスプレイを持参できる方までさまざまです。特に Windows IoT on RPi の場合には、もともと Raspberry Pi を使っている方は、それなりに機材が揃っている(自前のハット/拡張ボードとか)のでそれを使いたいものです。ちなみ、Raspberry Pi の既存のハットなのですが、Windows IoT のピン番号が異なっていたり融通が効かなかったりするので、ほとんど使えないと思います。I2C オンリーならば大丈夫かも、ってことで I2C ボードだけは検証します。残念ながら手元の Sparkfun のモータードライバは使えませんでした。

あと、歴史的に Raspberry Pi よりも Arduino のほうが早い時期からあるため、電子工作的なキットでは Arduino のほうが豊富にあります。このため RPi はサーバー機やカメラ利用に使われることが多いのです。Linux が動くので、そのまま apt-get で作れるってのがメリットですね。ですが、Windows IoT Core の場合は、どちらかというと Arduino 寄りな GPIO 接続を狙っているらしく、そのあたりが中途半端になっています。アナログ入力が RPi にはないので、温度センサーもちょっと工夫しないといけません。まあ、そのあたりは、Arduino + Raspberry Pi の組み合わせで試行するのがよいでしょう。ハンズオンでも話しましたが、中国の Arduino Nano 互換機は 300円と非常に安く手に入ります。Arduino Uno 互換機も 700円程度です。この値段ならば、Arduino を PIC などのチップと同様に扱うことも可能です(まあ、それでも 100円とかに比べれば十分高いですが)。

image

ハンズオンでは、敢えて安いブレッドボードと、あえて安いLED/ジャンパーワイヤーを使っています。色々高いものもあるのですが、価格的には低いところから入れますよというのを強調したかったからです。

こんな風な Arduino 戦車であれば、3,500円位で作れます。Arduno Nano 互換機を使えばもっと安くなります。これは Windows Remote Arduino 用にしたもので、Windows ストアアプリや Xamarin.Android を使って Android スマートフォンから操作できます。

image

ロボットアームも、おもちゃのものであれば 5,000円弱からあります。これは株式会社イスペットが出しているグリッパーアームロボットです。サーボモータではないので安く作れています。ロボットアームは2,3年前あたりに流行ったのですが、高価なものが多かった(5万円位?)ためか今ではほとんどが発売中止になっています。うまくやると、グリッパーアームで積木を積むことができます。

image

以前、Kickstarter で出ていた meArm も持ってきました。これはサーボモーターが4つ付いて 3,500円ぐらいです。英国産です。Raspberry Pi 用に Sparkfan で I2C ボードも買ってあるので、いずれ Windows IoT で動かしてみましょう。一年前に meArmPi の動作メモ | Moonmile Solutions Blog あたりで、Raspbian + mono + F# で動かしたことがあります。

image

半田付けの電子工作の例として、RealSense コンテストのとき作った妖怪ウォッチ LED も持って行きました。これは抵抗付きなので、そのまま Raspberry Pi のピンから給電しても大丈夫です。

image

私の場合、Internet of Things というよりも、動かせるモノからスタートしているので他とはちょっと毛色が違うのですが、具体的に「何かが動く」とか「何を操作できる」という入り口がいいかなと思っています。手元の Raspberry Pi 5 台は小学校でのワークショップ用に買ってはみたのですが、Windows IoT だとディスプレイがないとちょっと辛い感じ(Raspbian だとなおさら)なので、この辺りは考え直そうかなと思案中ですね。

次は何をするか?

Windows IoT Core で Lチカをやったり、モーターを制御できた後は何をするのか?が問題ですよね。ホビー的にマルチコプターに進むのもいいのですが、ワタクシ的には地べたを這いずり回るほうが「安全」なので、キャタピラと車輪とアームのほうに進みます。位置情報なんかは「確率ロボテック」を読むとよいでしょう。ルンバの位置情報はこれを使っているハズです。あと、カメラとアーム制御なんかは、ROS に対応させるのも良いかなと。

という訳で、.NET ラボ勉強会スタッフの皆様と、会場を提供してくださった Microsoft さんに感謝です。

参考資料

いくつか Windows IoT Core on Raspberry Pi で参考になりそうな書籍を上げておきます。

サンプルコード

ハンズオンで使った(使う予定だった)サンプルコードを後で載せておきます。

  • いわゆる Hello World.
  • Lチカ
  • 超音波センサーで(おざなりに)距離計測
  • グリッパーアームロボットを遠隔操作
  • Windows Remote Arduino で Arduino 戦車をストアプリで操作
  • Windows Remote Arduino で Arduino 戦車を Android スマートフォンで操作(Xamarin.Android)

の予定

 

カテゴリー: RaspberryPi, Win IoT | 1件のコメント

Firmata を使って Xamarin.Android から Arduino に接続(F#版)

Firmata を使って Xamarin.Android から Arduino に接続する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7185

これの F# 版を作ります。Xamarin.Android は主に C# で作ることが多いでしょうが、オール F# で作ることができます。Visual Studio 2013 では、Visual F# の Android テンプレートがあるので、そのまま使えます。

Firmata.NET を F# 版に書き直す

少し書き方が違いますが、ざっと書き下したのが以下のコードです。

namespace Firmata.NET

open System
open Android.App
open Android.Content
open Android.Runtime
open Android.Views
open Android.Widget
open Android.OS

open Android.Bluetooth
open System.Linq
open Java.Util

module ARDUINO = 
    let INPUT = 0
    let OUTPUT = 1
    let LOW = 0
    let HIGH = 1
    

type Arduino() as this =
    let INPUT = 0
    let OUTPUT = 1
    let LOW = 0
    let HIGH = 1

    let DIGITAL_MESSAGE = 0x90uy    // send data for a digital port
    let ANALOG_MESSAGE = 0xE0uy     // send data for an analog pin (or PWM)
    let REPORT_ANALOG = 0xC0uy      // enable analog input by pin #
    let REPORT_DIGITAL = 0xD0uy     // enable digital input by port
    let SET_PIN_MODE = 0xF4uy       // set a pin to INPUT/OUTPUT/PWM/etc
    let REPORT_VERSION = 0xF9uy     // report firmware version
    let SYSTEM_RESET = 0xFFuy       // reset from MIDI
    let START_SYSEX = 0xF0uy        // start a MIDI SysEx message
    let END_SYSEX = 0xF7uy          // end a MIDI SysEx message


    let mutable _socket:BluetoothSocket = null
    let mutable autoStart = false
    let mutable delay = 0

    let mutable digitalOutputData = Array.zeroCreate(16)
    let mutable digitalInputData = Array.zeroCreate(16)
    let mutable analogInputData = Array.zeroCreate(16)

    do
        if autoStart = true then
            delay <- 0 
            this.Connect()
            this.Open()

    /// Connect Bluetooth on Arduino.
    member this.Connect() =
        let adapter = BluetoothAdapter.DefaultAdapter
        if adapter = null then
            raise (Exception(&quot;No Bluetooth adapter found.&quot;))
        if adapter.IsEnabled = false then
            raise (Exception(&quot;Bluetooth adapter is not enabled.&quot;))
        let device = adapter.BondedDevices.FirstOrDefault( fun x -> x.Name = &quot;HC-06&quot; )
        if device = null then
            raise (Exception(&quot;Named device not found.&quot;))
        _socket <- device.CreateRfcommSocketToServiceRecord(UUID.FromString(&quot;00001101-0000-1000-8000-00805f9b34fb&quot;))
        _socket.Connect()

    member this.Open() =
        // let mutable command = Array.create<byte>(2)
        for i=0 to 5 do
            let command = [|
                REPORT_ANALOG ||| byte(i)
                1uy
            |]
            _socket.OutputStream.Write( command, 0, command.Length )
        for i=0 to 1 do
            let command = [|
                REPORT_DIGITAL ||| byte(i)
                1uy
            |]
            _socket.OutputStream.Write( command, 0, command.Length )

    member this.Close() = 
        _socket.Close()
        _socket <- null

    member this.digitalRead(pin:int):int =
        (digitalInputData.[pin >>> 3] >>> (pin &&& 0x07)) &&& 0x01
    member this.analogRead(pin:int):int =
        analogInputData.[pin]

    member this.pinMode(pin,mode) =
        let message = [|
            SET_PIN_MODE
            byte(pin)
            byte(mode)
        |]
        _socket.OutputStream.Write( message, 0, message.Length )

    member this.digitalWrite(pin,value) =
        let portNumber = (pin >>> 3) &&& 0xFF
        digitalOutputData.[portNumber] <-
            if value = 0 then
                digitalOutputData.[portNumber] &&& ~~~(1 <<< (pin &&& 0x07))
            else 
                digitalOutputData.[portNumber] ||| (1 <<< (pin &&& 0x07)) 
        let message = [|
            DIGITAL_MESSAGE ||| byte(portNumber) 
            byte(digitalOutputData.[portNumber] &&& 0x7F)
            byte(digitalOutputData.[portNumber] >>> 7)
        |]
        _socket.OutputStream.Write(message, 0, message.Length);
            
    member this.analogWrite(pin,value) = 
        let message = [|
            ANALOG_MESSAGE ||| (byte(pin) &&& 0x0Fuy)
            byte(value &&& 0x7F)
            byte(value >>> 7)
        |]
        _socket.OutputStream.Write(message, 0, message.Length);

    member this.setDigitalInputs( portNumber, portData ) =
        digitalInputData.[portNumber] <- portData

    member this.setAnalogInput( pin, value ) =
        analogInputData.[pin] <- value

定数が module を使っているのは愛嬌として、メッセージの配列を作るところは、直接作れるので若干楽ですね。ビット演算子が「&&&」や「|||」を使わないといけないので、文字数的に冗長なのは残念な感じがしますが、まあ、これはこれで良しということで。
int から byte へのキャストが頻発するのは、F# の宿命です。型を合わせないといけないので、アップキャストだけでなくダウンキャストに対しても、明示的な型のキャストが必要になります。このあたりインターフェースプログラミングをしているとちょっと冗長な感じになります。

マニフェストを設定する

Bluetooth を扱うための、パーミッションの設定は C# と同じです。

MainActivity

ざっと書いたので、ボタンのクリックイベントのところが雑ではありますが、C# よりも短くかけます。フィルタを多用する場合やフローチャート的に状態遷移する場合は、関数型言語 F# を使うとすんなりと書けるはずなんですけどね。このあたりは、Arduino 戦車に距離センサーを付けて自律化したときに試してみましょう。

type MainActivity () =
    inherit Activity ()

    let mutable buttonConnect:Button = null
    let mutable buttonOpen:Button = null
    let mutable buttonLedOn:Button = null
    let mutable buttonLedOff:Button = null
    let mutable arduino:Arduino = new Arduino()

    override this.OnCreate (bundle) =

        base.OnCreate (bundle)

        // Set our view from the &quot;main&quot; layout resource
        this.SetContentView (Resource_Layout.Main)

        // Get our button from the layout resource, and attach an event to it
        buttonConnect <- this.FindViewById<Button>(Resource_Id.buttonConnect)
        buttonOpen <- this.FindViewById<Button>(Resource_Id.buttonOpen)
        buttonLedOn <- this.FindViewById<Button>(Resource_Id.buttonLEDon)
        buttonLedOff <- this.FindViewById<Button>(Resource_Id.buttonLEDoff)


        buttonConnect.Click.Add( fun args -> 
            arduino.Connect();
            buttonConnect.Text <- &quot;connected.&quot;;
        )
        buttonOpen.Click.Add( fun args -> 
            arduino.Open();
            buttonConnect.Text <- &quot;Firmata opend.&quot;;
            arduino.pinMode(5, ARDUINO.OUTPUT );
            arduino.digitalWrite(5, ARDUINO.LOW);
        )
        buttonLedOn.Click.Add( fun e -> this.OnClickLedOn(buttonLedOn,e) )
        buttonLedOff.Click.Add( fun e -> this.OnClickLedOff(buttonLedOff,e) )

    member this.OnClickLedOn(sender,e) =
        arduino.digitalWrite(5, ARDUINO.HIGH)
    member this.OnClickLedOff(sender,e) =
        arduino.digitalWrite(5, ARDUINO.LOW)

動かす

見た目は C# 版と変わりませんが、F# のアプリが動いています。

Windows に乗せ換えて(実は、ストアアプリ版の Firmata ライブラリも作ってある)、F# からコマンドライン的に使えると、Ruby や Node.js のようにスクリプト言語のように使うことが可能です。このあたり、先の firmata のサイトに Haskell があるので、比較するのも面白いかなと(私は Haskell は全然ダメなんですけど)。

カテゴリー: Android, Arduino, F#, Xamarin | Firmata を使って Xamarin.Android から Arduino に接続(F#版) はコメントを受け付けていません

Firmata を使って Xamarin.Android から Arduino に接続する

Windows Remote Arduino を利用して Arduino 戦車を動かす | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7168

では、Windows がオープンソース化している Firmata ライブラリを利用して Arduino に接続したわけですが、Firmata プロトコル自体は公開されているので、どのような言語でも誰でも作れます。

firmata/arduino
https://github.com/firmata/arduino

github の readme を眺めると、.NET 実装もあります。ソースを見ていくと COM 経由で Arduino に USB ケーブルを刺して使うライブラリになっていますが、これをちょっと修正すれば Bluetooth のシリアル通信対応にできますよね。ということで、Bluetooth 2.0 のシリアル通信である RFCOMM を使って書き換えていきます。

Firmata.NET | imagitronics.org
http://www.imagitronics.org/projects/firmatanet/

2 つある中では、Firmata.NET のほうがコードが短かったので、これを利用します。このコードを使って、Android 上から Firmata を通して Arduino を操作できるようにしましょう。確か、ブラウザや Node.js から使うパターンが多いのですが、Xamarin.Android から C# で扱えるとネイティブアプリとして作れるので便利でしょう。ちなみに、コード自体は、短いので Xamarin の Starter 版(無償版)でも動作確認ができました。無償版の場合 128KB 制限なので、そのなかで収まっていると思われます(正確な大きさはわからない)。

RFCOMM 版の Firmata を作る

ざっくりと移植したのが以下です。Android の Bluetooth を使うために、BluetoothAdapter.DefaultAdapter を利用しています。

class Arduino
{
    public static int INPUT = 0;
    public static int OUTPUT = 1;
    public static int LOW = 0;
    public static int HIGH = 1;

    private const int MAX_DATA_BYTES = 32;

    private const int DIGITAL_MESSAGE = 0x90; // send data for a digital port
    private const int ANALOG_MESSAGE = 0xE0; // send data for an analog pin (or PWM)
    private const int REPORT_ANALOG = 0xC0; // enable analog input by pin #
    private const int REPORT_DIGITAL = 0xD0; // enable digital input by port
    private const int SET_PIN_MODE = 0xF4; // set a pin to INPUT/OUTPUT/PWM/etc
    private const int REPORT_VERSION = 0xF9; // report firmware version
    private const int SYSTEM_RESET = 0xFF; // reset from MIDI
    private const int START_SYSEX = 0xF0; // start a MIDI SysEx message
    private const int END_SYSEX = 0xF7; // end a MIDI SysEx message

    // private SerialPort _serialPort;
    private int delay;

    private int waitForData = 0;
    private int executeMultiByteCommand = 0;
    private int multiByteChannel = 0;
    private int[] storedInputData = new int[MAX_DATA_BYTES];
    private bool parsingSysex;
    private int sysexBytesRead;

    private volatile int[] digitalOutputData = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    private volatile int[] digitalInputData = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    private volatile int[] analogInputData = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    private int majorVersion = 0;
    private int minorVersion = 0;
    // private Thread readThread = null;
    private object locker = new object();

    /*
    Guid serviceGuid = Guid.Parse("00001101-0000-1000-8000-00805f9b34fb");
    RfcommDeviceService rfcommService;
    StreamSocket socket;
    DataWriter writer;
    DataReader reader;
    */

    BluetoothSocket _socket;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="serialPortName">String specifying the name of the serial port. eg COM4</param>
    /// <param name="baudRate">The baud rate of the communication. Default 115200</param>
    /// <param name="autoStart">Determines whether the serial port should be opened automatically.
    ///                     use the Open() method to open the connection manually.</param>
    /// <param name="delay">Time delay that may be required to allow some arduino models
    ///                     to reboot after opening a serial connection. The delay will only activate
    ///                     when autoStart is true.</param>
    public Arduino(string serialPortName, Int32 baudRate, bool autoStart, int delay)
    {
        /*
        _serialPort = new SerialPort(serialPortName, baudRate);
        _serialPort.DataBits = 8;
        _serialPort.Parity = Parity.None;
        _serialPort.StopBits = StopBits.One;
        */
        if (autoStart)
        {
            this.delay = delay;
            this.Connect();
            this.Open();
        }
    }

    /// <summary>
    /// Creates an instance of the Arduino object, based on a user-specified serial port.
    /// Assumes default values for baud rate (115200) and reboot delay (8 seconds)
    /// and automatically opens the specified serial connection.
    /// </summary>
    /// <param name="serialPortName">String specifying the name of the serial port. eg COM4</param>
    public Arduino(string serialPortName) : this(serialPortName, 115200, true, 8000) { }

    /// <summary>
    /// Creates an instance of the Arduino object, based on user-specified serial port and baud rate.
    /// Assumes default value for reboot delay (8 seconds).
    /// and automatically opens the specified serial connection.
    /// </summary>
    /// <param name="serialPortName">String specifying the name of the serial port. eg COM4</param>
    /// <param name="baudRate">Baud rate.</param>
    public Arduino(string serialPortName, Int32 baudRate) : this(serialPortName, baudRate, true, 8000) { }

    /// <summary>
    /// Creates an instance of the Arduino object using default arguments.
    /// Assumes the arduino is connected as the HIGHEST serial port on the machine,
    /// default baud rate (115200), and a reboot delay (8 seconds).
    /// and automatically opens the specified serial connection.
    /// </summary>
    public Arduino() : this(Arduino.list().ElementAt(list().Length - 1), 115200, false, 8000) { }


    public void Connect()
    {
        BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
        if (adapter == null)
        {
            throw new Exception("No Bluetooth adapter found.");
        }
        if (!adapter.IsEnabled)
        {
            throw new Exception("Bluetooth adapter is not enabled.");
        }
        BluetoothDevice device = (from bd in adapter.BondedDevices
                                    where bd.Name == "HC-06"
                                    select bd).FirstOrDefault();
        if (device == null)
        {
            throw new Exception("Named device not found.");
        }
        _socket = device.CreateRfcommSocketToServiceRecord(UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
        _socket.Connect();
        return;
    }

    /// <summary>
    /// Opens the serial port connection, should it be required. By default the port is
    /// opened when the object is first created.
    /// </summary>
    public void Open()
    {
        // _serialPort.Open();

        // Thread.Sleep(delay);

        byte[] command = new byte[2];

        for (int i = 0; i < 6; i++)
        {
            command&#91;0&#93; = (byte)(REPORT_ANALOG | i);
            command&#91;1&#93; = (byte)1;
            // _serialPort.Write(command, 0, 2);
            _socket.OutputStream.Write(command, 0, command.Length);
        }

        for (int i = 0; i < 2; i++)
        {
            command&#91;0&#93; = (byte)(REPORT_DIGITAL | i);
            command&#91;1&#93; = (byte)1;
            // _serialPort.Write(command, 0, 2);
            _socket.OutputStream.Write(command, 0, command.Length);
        }
        command = null;

        /*
        if (readThread == null)
        {
            readThread = new Thread(processInput);
            readThread.Start();
        }
        */
    }

    /// <summary>
    /// Closes the serial port.
    /// </summary>
    public void Close()
    {
        // readThread.Join(500);
        // readThread = null;
        // _serialPort.Close();
        _socket.Close();
        _socket = null;
    }

    /// <summary>
    /// Lists all available serial ports on current system.
    /// </summary>
    /// <returns>An array of strings containing all available serial ports.</returns>
    public static string[] list()
    {
        // return SerialPort.GetPortNames();
        return new string[] { "HC-06" };

    }

    /// <summary>
    /// Returns the last known state of the digital pin.
    /// </summary>
    /// <param name="pin">The arduino digital input pin.</param>
    /// <returns>Arduino.HIGH or Arduino.LOW</returns>
    public int digitalRead(int pin)
    {
        return (digitalInputData[pin >> 3] >> (pin & 0x07)) & 0x01;
    }

    /// <summary>
    /// Returns the last known state of the analog pin.
    /// </summary>
    /// <param name="pin">The arduino analog input pin.</param>
    /// <returns>A value representing the analog value between 0 (0V) and 1023 (5V).</returns>
    public int analogRead(int pin)
    {
        return analogInputData[pin];
    }

    /// <summary>
    /// Sets the mode of the specified pin (INPUT or OUTPUT).
    /// </summary>
    /// <param name="pin">The arduino pin.</param>
    /// <param name="mode">Mode Arduino.INPUT or Arduino.OUTPUT.</param>
    public void pinMode(int pin, int mode)
    {
        byte[] message = new byte[3];
        message[0] = (byte)(SET_PIN_MODE);
        message[1] = (byte)(pin);
        message[2] = (byte)(mode);
        // _serialPort.Write(message, 0, 3);
        _socket.OutputStream.Write(message, 0, message.Length);
        message = null;
    }

    /// <summary>
    /// Write to a digital pin that has been toggled to output mode with pinMode() method.
    /// </summary>
    /// <param name="pin">The digital pin to write to.</param>
    /// <param name="value">Value either Arduino.LOW or Arduino.HIGH.</param>
    public void digitalWrite(int pin, int value)
    {
        int portNumber = (pin >> 3) & 0x0F;
        byte[] message = new byte[3];

        if (value == 0)
            digitalOutputData[portNumber] &= ~(1 << (pin & 0x07));
        else
            digitalOutputData&#91;portNumber&#93; |= (1 << (pin & 0x07));

        message&#91;0&#93; = (byte)(DIGITAL_MESSAGE | portNumber);
        message&#91;1&#93; = (byte)(digitalOutputData&#91;portNumber&#93; & 0x7F);
        message&#91;2&#93; = (byte)(digitalOutputData&#91;portNumber&#93; >> 7);
        // _serialPort.Write(message, 0, 3);
        _socket.OutputStream.Write(message, 0, message.Length);
    }

    /// <summary>
    /// Write to an analog pin using Pulse-width modulation (PWM).
    /// </summary>
    /// <param name="pin">Analog output pin.</param>
    /// <param name="value">PWM frequency from 0 (always off) to 255 (always on).</param>
    public void analogWrite(int pin, int value)
    {
        byte[] message = new byte[3];
        message[0] = (byte)(ANALOG_MESSAGE | (pin & 0x0F));
        message[1] = (byte)(value & 0x7F);
        message[2] = (byte)(value >> 7);
        // _serialPort.Write(message, 0, 3);
        _socket.OutputStream.Write(message, 0, message.Length);
    }

    private void setDigitalInputs(int portNumber, int portData)
    {
        digitalInputData[portNumber] = portData;
    }

    private void setAnalogInput(int pin, int value)
    {
        analogInputData[pin] = value;
    }

    private void setVersion(int majorVersion, int minorVersion)
    {
        this.majorVersion = majorVersion;
        this.minorVersion = minorVersion;
    }

    /*
    private int available()
    {
        return _serialPort.BytesToRead;
    }
    */
} // End Arduino class

接続あたりは、

Android から Bluetooth+RFCOMM を利用してモーター制御をする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6826

と同じように書いています。独自に RFCOMM を使った場合は自前で Android/Arduino のプロトコルを作らなければいけませんが(とはいえ、自分の場合は 8 バイト固定にしてあるの簡単)、Firmata プロトコルを使うと、GPIO 等をそのまま使う分には手軽です。
バイナリ送信をしているとこもそのまま移植。今回はテスト的なものなので、Android の受信側は省略しました。もうちょっと整理して、そのうち github へ。

マニフェストを設定する

Bluetooth を扱うので、パーミッションを設定しておきます。
たぶん、”BLUETOOTH” だけチェックすれば ok です。

UI と MainActivity

こんな画面を作っておきます。

5ピンに LED をつけるので、pinMode などを設定します。

public class MainActivity : Activity
{
    Arduino arduino;
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        // Get our button from the layout resource,
        // and attach an event to it
        arduino = new Arduino();
        FindViewById<Button>(Resource.Id.buttonConnect).Click += (s, e) => { 
            arduino.Connect();
            FindViewById<Button>(Resource.Id.buttonConnect).Text = "connected.";
        };
        FindViewById<Button>(Resource.Id.buttonOpen).Click += (s, e) => { 
            arduino.Open();
            FindViewById<Button>(Resource.Id.buttonOpen).Text = "Firmata opend";
            arduino.pinMode(5, Arduino.OUTPUT);
            arduino.digitalWrite(5, Arduino.LOW);
        };

        FindViewById<Button>(Resource.Id.buttonLEDon).Click += OnClickLedOn;
        FindViewById<Button>(Resource.Id.buttonLEDoff).Click += OnClickLedOff;
    }

    void OnClickLedOn(object sender, EventArgs e)
    {
        arduino.digitalWrite(5, Arduino.HIGH);
    }
    void OnClickLedOff(object sender, EventArgs e)
    {
        arduino.digitalWrite(5, Arduino.LOW);
    }
}

Connect と Open は同時にやってもいいと思います。RFCOMM へのアクセスを Sync のほうの非同期関数を使えばよかったのですが、ひとまず同期的に作っています。まあ、受信回り(温度や湿度データとか)をきちんと作って、await/async を使えば結構すっきりするハズです。

実行してみる

ビルドをして実機で実行してみます。うちの Android は 4.1.2 という古いタイプなのですが正常に動作しました。Bluetooth 経由なので、アクセスポイントとかが必要ないので戸外でも使えますよね。まあ、戸外で使って、どうということはないのですが。

これはこれで整理して、後で Arduino 戦車も動かせるように組み直しいきましょう。あと、適当な距離センサーや加速度センサーを付けて、値をとれるようにいておきます。

カテゴリー: Android, Arduino, Xamarin | Firmata を使って Xamarin.Android から Arduino に接続する はコメントを受け付けていません

Windows Phone 10 から Windows Remote Arduino を利用する

Windows Remote Arduino を利用して Arduino 戦車を動かす | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7168

先日の続きで、Windows Phone 10 のユニバーサルアプリで Windows Remote Arduino を使います。UI は、Windows ストアアプリで作ったものをそのまま流用します。Windows 10 上の Visual Studio 2015 RC ならば、そのまま XAML デザイナが動くのですが、手元の PC では Windows 8.1 上に VS2015RC を入れているのでデザイナが動きません…が、基本的なところは 8.1 の XAML と同じなのでそのままコピーして使えます。

ms-iot/remote-wiring の Microsoft.Maker.win10 のほうをコピーして使います。

image

Package.appxmanifest ファイルに、bluetooth.rfcomm の記述を追加しておきます。

 

 
  <Capabilities>
    <Capability Name=&quot;internetClient&quot; />
    <DeviceCapability Name=&quot;bluetooth.rfcomm&quot;>
      <Device Id=&quot;any&quot;>
        <Function Type=&quot;name:serialPort&quot; />
      </Device>
    </DeviceCapability>
  </Capabilities>

 

8.1 のときと違って、m2: のプレフィックスが要りません。これを、ARM でビルドして、Windows Phone 10 に送り込みます。手元では Lumia 1520 で確認しています。

https://pbs.twimg.com/media/CFTG3JGUIAA-SJe.jpg

あらかじめ、Bluetooth とのペアリングをしておく必要があるのは Windows ストアアプリのときと同じです。初回起動時のみ接続先の Bluetooth を使うかどうかのダイアログが表示されます。

コードは、Windows Remote Arduino を利用して Arduino 戦車を動かす | Moonmile Solutions Blog のものと全く同じなので省略。ユニバーサルアプリで作ったので、そのままデスクトップの Windows 10 でも動くはずです(動作は確認していませんが)。そんなわけで、ひとまず、Windows ストアアプリと Windows Phone アプリから動作したので完了。

これを C# 版にして、Xamarin.Android で動くようにすれば、Android から Firmata を使って Arduino を操作することも可能になりますね…と、まずは C++ から C# への移植作業をしないといけないのですが、それはまた後日。

カテゴリー: Arduino, Win IoT, Windows Phone | Windows Phone 10 から Windows Remote Arduino を利用する はコメントを受け付けていません

Windows Remote Arduino を利用して Arduino 戦車を動かす

Windows Remote Arduino を Arduino Uno/Nano で試す | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7158

の続きです。Arduino に Firmata を入れることによってクロスプラットフォームで Arduino をリモート操作することが可能です。ということで、手元にある Arduino 戦車に Firmata を入れ直し、Windows Remote Arduino を使ってタブレットから動かしてみます。

L293D 利用して Arduino 戦車を作る | Moonmile Solutions Blog で作った Arduino 戦車は自前で シリアル通信部分を書いているのですが、Firmata のフォーマットに直せば、Ruby とか Node.js からも動かせるようになりますよね。そのあたりの相互通信は Alljoyn でやるようになるハズなのですが、ひとまず Firmata で動かします(デファクトスタンダードっぽいし)。

image

こんな感じの UI を作って、ボタンをタップして Arduino 戦車を動かします。コードで、AddHandler しているのは、ボタンをタップしている間だけモーターを動かしたかったからです。途中で通信が暴走すると、戦車自体が暴走してしまうので、デフォルトで戦車が止まるようにしました。

プロジェクト構成は、Microsoft.Maker.* をソリューションに加えてビルド。メインの ArduinoTankFirmata プロジェクトから各プロジェクトを参照設定しておきます。

image

ざっと書いたのが以下のコードです。Arduino 戦車の Bluetooth モジュールの名前が「HC-05」になっています。モータ駆動のためのピン(5-7,8-10)をいちいち制御しないといけませんが、Arduino の Sketch と同じように書くことができます。

 
public sealed partial class MainPage : Page
{
    BluetoothSerial bluetooth;
    RemoteDevice arduino;

    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;

        foreach (var b in new Button[] { ledRed, ledYellow, ledBlue })
        {
            b.AddHandler(PointerPressedEvent, new PointerEventHandler(OnLedPressed), true);
            b.AddHandler(PointerReleasedEvent, new PointerEventHandler(OnLedReleased), true);
        }
        tankStop.Click += (s, e) => { MotorLeftStop(); MotorRightStop(); };
        foreach (var b in new Button[] { tankForward, tankBack, tankLeft, tankRight })
        {
            b.AddHandler(PointerPressedEvent, new PointerEventHandler(OnTankPressed), true);
            b.AddHandler(PointerReleasedEvent, new PointerEventHandler(OnTankReleased), true);
        }

    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        bluetooth = new BluetoothSerial(&quot;HC-05&quot;);
        arduino = new RemoteDevice(bluetooth);
        bluetooth.ConnectionEstablished += bluetooth_ConnectionEstablished;
        bluetooth.begin(9600, SerialConfig.SERIAL_8N1);
    }

    async void bluetooth_ConnectionEstablished()
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
            this.textStatus.Text = &quot;接続しました&quot;;
        });
    }

    const int pinLedRed = 13;
    const int pinLedYellow = 12;
    const int pinLedBlue = 11;

    const int pinMotorAPower = 10;
    const int pinMotorAOut1 = 9;
    const int pinMotorAOut2 = 8;
    const int pinMotorBPower = 5;
    const int pinMotorBOut1 = 6;
    const int pinMotorBOut2 = 7;

    void OnLedPressed(object sender, PointerRoutedEventArgs e)
    {
        if (sender == ledRed) arduino.digitalWrite(pinLedRed, PinState.HIGH);
        if (sender == ledYellow) arduino.digitalWrite(pinLedYellow, PinState.HIGH);
        if (sender == ledBlue) arduino.digitalWrite(pinLedBlue, PinState.HIGH);
    }
    void OnLedReleased(object sender, PointerRoutedEventArgs e)
    {
        if (sender == ledRed) arduino.digitalWrite(pinLedRed, PinState.LOW);
        if (sender == ledYellow) arduino.digitalWrite(pinLedYellow, PinState.LOW);
        if (sender == ledBlue) arduino.digitalWrite(pinLedBlue, PinState.LOW);
    }

    void OnTankPressed(object sender, PointerRoutedEventArgs e)
    {
        if (sender == tankForward) { MotorLeftForward(); MotorRightForward(); };
        if (sender == tankBack) { MotorLeftBack(); MotorRightBack(); };
        if (sender == tankLeft) { MotorLeftBack(); MotorRightForward(); };
        if (sender == tankRight) { MotorLeftForward(); MotorRightBack(); };

    }
    void OnTankReleased(object sender, PointerRoutedEventArgs e)
    {
        MotorLeftStop();
        MotorRightStop();
    }

    void MotorLeftStop()
    {
        arduino.digitalWrite( pinMotorAPower, PinState.LOW );
        arduino.digitalWrite(pinMotorAOut1, PinState.LOW);
        arduino.digitalWrite(pinMotorAOut2, PinState.LOW);
    }
    void MotorLeftForward()
    {
        arduino.digitalWrite(pinMotorAPower, PinState.HIGH);
        arduino.digitalWrite(pinMotorAOut1, PinState.LOW);
        arduino.digitalWrite(pinMotorAOut2, PinState.HIGH);
    }
    void MotorLeftBack()
    {
        arduino.digitalWrite(pinMotorAPower, PinState.HIGH);
        arduino.digitalWrite(pinMotorAOut1, PinState.HIGH);
        arduino.digitalWrite(pinMotorAOut2, PinState.LOW);
    }
    void MotorRightStop()
    {
        arduino.digitalWrite(pinMotorBPower, PinState.LOW);
        arduino.digitalWrite(pinMotorBOut1, PinState.LOW);
        arduino.digitalWrite(pinMotorBOut2, PinState.LOW);
    }
    void MotorRightForward()
    {
        arduino.digitalWrite(pinMotorBPower, PinState.HIGH);
        arduino.digitalWrite(pinMotorBOut1, PinState.LOW);
        arduino.digitalWrite(pinMotorBOut2, PinState.HIGH);
    }
    void MotorRightBack()
    {
        arduino.digitalWrite(pinMotorBPower, PinState.HIGH);
        arduino.digitalWrite(pinMotorBOut1, PinState.HIGH);
        arduino.digitalWrite(pinMotorBOut2, PinState.LOW);
    }
}

このプログラムを、去年の de:code で貰った Toshiba のタブレットに入れて動作確認してます。Toshiba タブレットに Windows 10 IP を入れようとしたのですが、失敗してしまったので、Windows 8.1 になります。

https://pbs.twimg.com/media/CFJIDyFVEAAvMYf.jpg

ライントレースのような自律的な動きをする場合は、Firmata 経由では冗長な処理になっていしまうのですが(センサーを BT で飛ばして PC で制御するというスタイルになるので)、Arduino のセンシングデータをそのまま送ってきたり、今回のようにユーザーが直接コントロールする場合には、さっくりと作れてよさそうです。GPIO の使い方は Sketch に似ているんですが、個人的には .NET micro 風にしてもよかったのでは?と思っていますが。まあ、そのあたりは自分で拡張メソッドを作ればよいわけで、その辺はいずれ。

カテゴリー: Arduino, Win IoT | Windows Remote Arduino を利用して Arduino 戦車を動かす はコメントを受け付けていません

Windows Remote Arduino を Arduino Uno/Nano で試す

ホーム – 組込みシステム開発技術展 | ESEC の開催中だし、de:code の準備で忙しいのか、誰もがスルーしたままの Windows Remote Arduino を試してみました。

image

Windows Remote Arduino が何をするものかを簡単に言うと、Arduino にサーバーを入れて、Bluetooth 経由などで Windows 8.1/10 や Windows Phone から GPIO などを操作しようという仕組みです。Arudino 側には Firmata というライブラリがあって、すでに色々な言語(Ruby とか Javascript/Node.js など)からアクセスできるようになっています。Arduino IDE では、「ファイル」→「スケッチの例」→「Firmata」→「StandardFirmata」で、コードが開けるのでビルドしてインストールができます。

部品を準備する

サイトを見れば、さっくりと動く?のかどうかはわかりませんが、まあ試行錯誤をすれば30分程で動くようにはなります。手持ちの Arduino と Bluetooth モジュールによってちょこちょことソースを書き換えないといけないのが、嵌りどころではあるのですが。

  • Visual Studio 2013/2015RC
    Microsoft.Maker のライブラリをビルドするに必要です。本ブログで解説するコードは Windows 8.1/VS2013 で動作確認しています。
  • Windows 側の Bluetooth ドングル
    ノート PC だったりすると内蔵なんですが、据え置き PC だとドングルを刺して。
  • Arduino Uno など。
    Firmata が動けば何でも。
  • Bluetooth モジュール
    Microsoft のサイトでは、SparkFun Bluetooth Mate Silver を使っていますが何でもいいです。私は手持ちの HC-05/06 で動作確認しています。BT2.0 のシリアル通信を使うので BLE の必要はありません。
  • ブレッドボード、ジャンパーワイヤー、LED など
    ここは電子工作的に。

Arduino に Firmata を焼く

「ファイル」→「スケッチの例」→「Firmata」→「StandardFirmata」を開いて、ビルドします。

image

この時、Arduino 側に差し込む Bluetooth のシリアル通信速度を設定し直します。デフォルトでは、57600 のように高速になっているので、HC-05 の初期値の 9600 に設定し直します。このあたりは、手持ちの Bluetooth モジュールに合わせます(私の AT コマンドが面倒で、そのまま 9600 で使っています)

Firmata.begin(9600);

Windows Remote Arduino のライブラリをダウンロードする

Windows IoT – SetupPCWRA の Option 1: Install the NuGet package を使えば、一発で Nuget で取れるようなことが書いてありますが…まだ、NuGet 上にはありません(Windows 8.1からも見つからない)。仕方がないので github からダウンロードしてビルドをします。

ライブラリは、以下からダウンロードできます。win8.1 と win10 の違いは、win8.1 が従来の Windows ストアと Phone のユニバーサルアプリで作ってあって、win10 は新しい Universal なプロジェクトで作ってあります。どちらもライブラリとし使うだけなので、UI は必要ないのですが、どうやらコードが C++/CX で書いているためなのか、8.1 では PC と Phone とのライブラリを別々に作らないとだめなようです。win10 のほうはひとつにまとまっています。

ms-iot/remote-wiring · GitHub
https://github.com/ms-iot/remote-wiring

これと、Lチカをさせるためのコードをダウンロードします。

ms-iot/windows-remote-arduino-samples · GitHub
https://github.com/ms-iot/windows-remote-arduino-samples

内部的に RFCOMM を使っているのでマニフェストファイルを手作業で修正する必要があります。remote-wiring のプロジェクトを参照させるために、remote-wiring フォルダにライブラリのプロジェクトを丸ごとコピーしておきます。

 

public MainPage()
{
    this.InitializeComponent();
    // bluetooth = new BluetoothSerial("RNBT-E072");
    bluetooth = new BluetoothSerial("HC-06");

    arduino = new RemoteDevice(bluetooth);
    bluetooth.ConnectionEstablished += OnConnectionEstablished;

    //these parameters don't matter for bluetooth
    bluetooth.begin(9600, SerialConfig.SERIAL_8N1);
}

 

ここでも、シリアル通信の速度「9600」と、Bluetooth モジュールの名前「HC-05」のように設定し直しておきます。あとは、x86 あたりでビルドをすれば ok です。x64 でビルドをすると XAML デザイナが動かないので、ひとまず x86 で動作確認するとよいでしょう。Any CPU で動かないのは C++/CX を使っているためです。

ちなみにLチカさせているところはこんな感じ。RemoteDevice クラスで取得した arduino オブジェクトを使って、digitalWrite してます。このあたりは Android の Scketch に似せているみたい。
個人的には .NET Micro に寄せたほうが良いのでは?と思うのですが、そこは OSS なので自己拡張で。

private void OnConnectionEstablished()
{
    //enable the buttons on the UI thread!
    var action = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler( () => {
        OnButton.IsEnabled = true;
        OffButton.IsEnabled = true;
    }));
}

private void OnButton_Click( object sender, RoutedEventArgs e )
{
    //turn the LED connected to pin 5 ON
    arduino.digitalWrite( 5, PinState.HIGH );
}

private void OffButton_Click( object sender, RoutedEventArgs e )
{
    //turn the LED connected to pin 5 OFF
    arduino.digitalWrite( 5, PinState.LOW );
}

Bluetooth とペアリングする

Windows 8.1 のとき、BT2.0を接続する場合はペアリングが必要です。Windows 10 で BLE 接続する場合はペアリングなしでいけるらしいのですが、そこはまだ確認してません。

image

HC-05 のピンコードは「1234」です。

Arduino Uno で動かす

Bluetooth モジュールへの配線は MS のサイト で確認してもらうとして(RX/TXを接続するだけの簡単なものです)Uno 互換機で動かした結果がこんな感じです。

https://pbs.twimg.com/media/CFAp8IxUgAA88WX.jpg:large

デスクトップのストアアプリから Lチカができますね。まあ、Lチカだけやっても面白くないので、後でモーターかサーボモーターを動かしてみましょう。センサーの類も取得できるはずですね

Arduino Nano で動かす

USB 給電ではなくて、乾電池から給電させて Arduino Nano で動かします。これは、互換機なんで 300円前後のものです。Firmata + Windows の組み合わせが、手軽に?Windows Remote Android で実現ができます、ってことですね。

https://pbs.twimg.com/media/CFA0RLJUEAAlymU.jpg:large

ここでは、PC から扱っているわけですが、Windows Phone 経由にもできるだろうし、あるいは Ruby や Node.js を使っても同じインターフェースでできるということです。そのあたりは、クライアントアプリの切り替えが自由になって、便利な仕組みです。

カテゴリー: Arduino, Win IoT | Windows Remote Arduino を Arduino Uno/Nano で試す はコメントを受け付けていません

Win IoT に固定IPを指定する

ハンズオン用に RPi2 を持っていくのですが、現状の Win IoT では WiFi が使えません。MS 品川会場でホットスポットが使えないので、ローカルネットワークを組むことになりますが、DHCP でしか IP を割り振るとなるとルータが必要になって結構大変なので、固定 IP を指定するようにします。そうすると、ハブだけでいけます。インターネットには繋がりませんが。

GUI で設定できるような項目はないのですが、Win IoT に PowerShell でログインができるので、これを使います。

Win Iot on RPi にログインする

ログイン自体は以下のようにします。このあたり、powershell を知っていないと難しいんですが…知っていれば簡単なものなのでしょうか?

Enter-PsSession -ComputerName minwinpc -Credential minwinpc\Administrator

アドレスを確認する

PowerShell で IP アドレスの設定
https://www.upken.jp/kb/powershll-setipaddress.html

を参考にしながら設定していきます。上記のサイトでは IPv6 ですが、IPv4 に読み替えれば設定ができます。

Get-NetIPAddress で InterfaceIndex を確認します。大抵の場合、2 です。

Get-NetIPAddress

IPAddress         : 172.16.0.8
InterfaceIndex    : 2
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Dhcp
SuffixOrigin      : Dhcp
AddressState      : Preferred
ValidLifetime     : 23:03:26
PreferredLifetime : 23:03:26
SkipAsSource      : False
PolicyStore       : ActiveStore

固定IPを設定する

New-NetIPAddress で設定をします。PrefixOrigin が “Manual” になることでわかります。

New-NetIPAddress -InterfaceIndex 2 -IPAddress "172.16.0.99" -PrefixLength 24

IPAddress         : 172.16.0.99
InterfaceIndex    : 2
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : 172.16.0.99
InterfaceIndex    : 2
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : PersistentStore

ネットマスクを 255.255.255.0 にする場合は 24 にします。

固定IP を変更する

Remove-NetIPAddress した後に New-NetIPAddress します。逆でも IP を指定すれば大丈夫みたいです。

DHCP に戻す

DHCP が割り振る IP に戻すには Set-NetIPInterface を使います。PrefixOrigin が “Dhcp” に戻ります。

Set-NetIPInterface -InterfaceIndex 2 -AddressFamily IPv4 -Dhcp Enabled

IPAddress         : 172.16.0.8
InterfaceIndex    : 2
InterfaceAlias    : Ethernet
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 24
PrefixOrigin      : Dhcp
SuffixOrigin      : Dhcp
AddressState      : Preferred
ValidLifetime     : 23:59:47
PreferredLifetime : 23:59:47
SkipAsSource      : False
PolicyStore       : ActiveStore

コンピュータ名を変える

以下のように setcomputername で変更ができます。名前を確認したいときは hostname です。

setcomputername minwinpc1 

このあたりは、Linux と似ているので想像がつくんですけどね。Linux コマンドとの対応表がほしい。

カテゴリー: RaspberryPi, Win IoT | Win IoT に固定IPを指定する はコメントを受け付けていません