poipoiです。

技術的ななにかと、そうじゃないなにか。

iPadで「ゼロから作るDeep Learning」を勉強するために必要なこと

わりとニッチな話。

iPad Pro を買ってからというもの、Pythonista3 という iPad 用開発環境で python を書いてます。

Pythonista 3

Pythonista 3

  • omz:software
  • 仕事効率化
  • ¥1,200

で、最近話題のこちらの書籍を iPad を使って勉強しようと思ったのですが、見事にハマったので備忘録。

問題点

本書の学習につかうサンプルソース群がこちらに公開されているのですが

GitHub - oreilly-japan/deep-learning-from-scratch: 『ゼロから作る Deep Learning』のリポジトリ

Pythonista3 からだと MNIST データの読み込みの際にエラーを起こしてしまって使用できません。

なので、ソース群を Pythonista3 用に改造してあげる必要がありました。

先に結論

こちらに改造済みサンプルソース群をアップしてあります。

github.com

使用方法としては、Pythonista3 から StaSh を使って、

wget -o samples.zip https://github.com/poipoi/deep-learning-from-scratch/archive/master.zip
unzip samples.zip 

すればOKです!

細かい話

そもそもなんでエラーが起きていたかというと、内部で使用している pickle が NumPy 配列に対応していないバージョンだったからっぽいです。

なので、NumPy 配列から一旦標準のリストに変換してあげて pickle.dump, pickle.load するように変更しました。

        dataset = _load_img(key_file[key]).tolist()
        with open(get_save_file_path(key), 'rb') as f:
            dataset[key] = np.array(pickle.load(f))

また全データを一気に読み込むとメモリ不足で Pythonista3 が落ちてしまったので

各サンプルデータ毎に生成する pickle データを分けてあげることで落ちないようにすることができました。 (ここら辺は環境によるかも。。。)

    dataset = {}
    for key in key_file.keys():
        if not os.path.exists(get_save_file_path(key)):
            init_mnist(key)
        
        with open(get_save_file_path(key), 'rb') as f:
            dataset[key] = np.array(pickle.load(f))

おわり。

参考サイト

「ゼロから作るDeep Learning」をiPhoneのPythonistaだけで学ぶ(2) - blog.tmp.online

Unityで遺伝的アルゴリズムやってみた

f:id:poipoides:20170204212506g:plain

Unityくらいそろそろ使えないとマズそうな気がしてきて勉強を開始してみたところ、

「あれ?これって物理シミュレータ代わりとして意外といけてるんじゃね?」

「前からやりたかった物理シミュレータ x 遺伝的アルゴリズムできるんじゃね?」

ってなったので試しに作ってみたやーつ。

遺伝的アルゴリズムとは?

遺伝子が進化していく様子を模した近似解の探索アルゴリズム

まあ要はいろんな個体を用意して、試して、優秀な子を残して進化させていくみたいな。

詳しくは Wiki みてください。

で、これをゲームとかシミュレータとかに適用してる人が結構いて、 そういう動画を見るのが結構好きだったんですね。

Flexible Muscle-Based Locomotion for Bipedal Creatures from John Goatstream on Vimeo.

普通は割とガチな物理シミュレータ(OpenDynamicEngineとかJigLibXとか)でやるっぽいんだけど、

Unity でサクッと作れたらお得じゃない?ってことで試しに作ってみた。

結果

GA001a from poipoi on Vimeo.

  • コースの形はアルファベットの中でも一番周りにくそうな「B」の形。
  • 死亡条件は「壁にぶつかる」or 「遅い速度がしばらく続いたら」。
  • 移動した総距離を優劣の判断材料とした。
  • 遺伝子は車をコントロールするためのボタン(上下左右)の押し方の羅列。

一応できたっぽい。

ただしばらくすると結果が頭打ちになっちゃうのでぐるぐる何周も回るまでには至らない。 f:id:poipoides:20170204211637p:plain (一番上のグラフが各世代のトップの遺伝子の成績。1周半くらいで頭打ちっぽい。)

多分パラメーターとかアルゴリズムを調整しないとなんだろうなーと思う。

締め

ヒマ(と要望が)あったらもう少しちゃんとしたアルゴリズム解説書く。

一応コードは以下に挙げておく。

github.com

おわり。

モダンC++erになりたい! (C++11編)

qiita.com

こんな記事がバズっている昨今。。。 (ワイ、C++初心者や…。)

そろそろモダンC++erにならないといけないと思い立ち、いろいろと勉強。

とりあえずはC++11まわりの記述を中心に備忘録的にまとめようかと。

※ (C++11) って書いてあるヤツがC++11から登場した機能。

コンテナ

ポイント:

  • 配列とか割と面倒なことが多いのでコンテナを積極的に使う。

array (C++11)

vector

unordered_map (C++11)

auto と range based for

ポイント:

  • auto を使うと型を推論してくれるので、ながったらしいクラス名などを省略できる。
  • range based for を使うとイテレーターを使ったループの記述がラクになる。

auto (C++11)

range based for (C++11)

スマートポインタ

ポイント:

  • 生のポインタは領域破棄のしどころが難しいため、必要がなくなった際に自動で破棄してくれるスマートポインタを用いる。
  • auto_ptr はコピーによる所有権移動に問題があるため、C++11からは非推奨。
  • unique_ptr は参照元が 1つの場合に用いる。(というかそれ以上扱えない仕組みになってる)
  • shared_ptr は参照元が複数になる場合に用いる。
  • weak_ptr は shared_ptr で管理している領域を管理対象にならずに参照したい場合に用いる。
  • nullptr が使えるようになった

unique_ptr (C++11)

  • NG例

shared_ptr (C++11)

  • unique_ptr との違い
  • NG例

weak_ptr (C++11)

  • weak_ptr での参照の仕方

nullptr (C++11)

function、ラムダ式

ポイント:

  • 関数ポインタの代わりに function で関数を扱えるようになった
  • ラムダ式を扱えるようになり無名関数・クロージャが使用できるようになった

function (C++11)

ラムダ式 (C++11)

  • 基礎編
  • キャプチャについて

Boost.Range とか使うともうちょっと直感的に書けるみたいだけど、一応関数型言語の真似事的なこともできる。

THETA S x vvvv でリアルタイム360°ビューワーつくった話

こちらは vvvv Advent Calendar 2015 の 16日目の記事です。

今日は vvvv と THETA S ネタ。

この界隈(?)ならみんなが持っているであろう 360°全天球カメラ「RICOH THETA」シリーズ。

この THETAシリーズに、この秋新たに「THETA S」が加わりました!

https://theta360.com/ja/about/theta/s.html

旧モデルから画質が改善されたり、いろいろと変更点はあるのですが、

注目したいのは、「ライブストリーミング」モードが追加されたことです!

THETA S のライブストリーミングモード

THETA S の本体下部には micro USB 端子があり、USBケーブルで PC と接続することによって、Webカメラと同じように THETA S のリアルタイムな映像を取得することができるのです!

今までは撮影専用でしたが、このモードが追加されたことで用途がぐっと広がるのでは!と期待しています。

問題点

ですが、問題点が1つ。。。

実はライブストリーミングモードで取得できる画像は以下のようになっているのです。

f:id:poipoides:20151209175002j:plain

THETA 両サイドにあるレンズからの映像が「そのまま」表示されているだけなのです。本当は↓のような 360°ぐるっと好きな位置で見れるようにしたいのに。。。


360° cockpit view | Fighter Jet | Patrouille Suisse ...

こうするためには THETA S から取得できる画像をリアルタイムで 360° 用に変換してあげる必要があるのです!

本題

と、いうことで vvvv で THETA S リアルタイム 360°ビューワーを作ってみました!

ただし、後述しますが制作上の制約がありまして、今回は vvvv x64 + DirectX 11 環境のみの対応になります。

使用方法

細かい技術的な解説は一旦後回しにして、まずは使用方法です。

まずは今回作成した ThetaSTexture をこちらからダウンロードしてください

https://github.com/poipoi/ThetaSTexture/releases

以下、使うためのステップです。

  1. vvvv x64 に Direct X11 パックを入れた環境を用意
  2. コントリビューション用フォルダを用意してパスを通す
  3. コントリビューション用フォルダに「texture11」というフォルダを作成
    • 公式では「effects」「plugins」「modules」の3つを用意しろと書いてますが、それと同じレイヤーに「texture11」を作ってください。
  4. 上述のURLより ThetaSTexture をzipでダウンロード
  5. zip を解凍しフォルダごと「texture11」フォルダ内に入れる

これで使用できます!

実例

今回作成した ThetaSTexture にはヘルプ用パッチもつけてありますので、そちらを見てみましょう!

こんな感じで THETA S からの映像を

  • THETA S から読み込んできた変換前の状態
  • パノラマ状テクスチャへ変換した状態
  • それを元に Cube Map した状態

の3つがそれぞれ見て取れると思います。

解説

ここからは、技術解説です。

ものすごい簡単にいうと、

  1. ピクセルシェーダーで THETA S からの画像をパノラマ画像に変換する。
  2. パノラマ画像をつかって Cube Map する。

ということをやっています。

2 については普通に vvvv に元からあるパッチで実現できるので説明は割愛します。なので、今回の制作のメインは 1 の部分になります。

ちなみに、今回シェーダー素人+算数苦手な私が挑戦したので、いろいろ間違えや周りくどい部分がありそうな気がしてます。もし変なとこあったらご指摘大歓迎です。

変換の手順

THETA S の画像からパノラマ画像に変換するだけというと簡単に聞こえますが、ピクセルシェーダー内だけでも結構複雑な手数を踏んで変換を行っています。

簡単に説明すると以下の様な感じです。

f:id:poipoides:20151209165114g:plain

  1. パノラマ画像の各ピクセルを全天球の緯度経度に変換
  2. 緯度経度を THETA S レンズ上のXY座標(正射影)に変換
  3. 正射影のXY座標から立体射影のXY座標に変換
  4. 立体射影の座標を THETA S 上の座標に変換。

THETA S の画像をパノラマに変換するんだから順序が逆じゃない?と思い方もいるかと思いますが、ピクセルシェーダーはアウトプット画像上の座標がインプット画像上の座標のどこに当たるかを考えて計算します。

なので、アウトプット座標(パノラマ画像上の座標)→インプット座標(THETA S 画像上の座標)方向の変換を考えていくことになります。

1. パノラマ画像の各ピクセルを全天球の緯度経度に変換

f:id:poipoides:20151209165244g:plain

パノラマ画像上の各ピクセルが、全天球上の表面のどこに当たるかを計算し、その緯度経度を算出します。

パノラマ画像は正距円筒図法であると仮定。 (くわしくはここ参照)

なので単純に以下のような対応になります。

経度:λ = x
緯度:φ = y

2. 緯度経度を THETA S レンズ上のXY座標(正射影)に変換

f:id:poipoides:20151209165305g:plain

全天球上の緯度経度の値を、THETA S のレンズで正射影したときの座標に変換します。

変換式は以下。

X座標(正射影):xt = cos(λ) * sin(φ)
Y座標(正射影):yt = sin(φ)

3. 正射影のXY座標から立体射影のXY座標に変換

f:id:poipoides:20151209165318g:plain

THETA S のレンズは立体射影らしいので(ソースはここ)、正射影の座標を立体射影の座標に変換しなければいけません。

正射影を立体射影に変換するには、

XY座標(正射影)→ 極座標(正射影)→ 方位角・仰角 → 極座標(立体射影) → XY座標(立体射影)

というように計算してあげるといいような気がします。

(参考にしたのはこちら

ちなみに極座標上の偏角と方位角は同じになりますので仰角のみを計算する形になります。

偏角 = 方位角:θ = arctan(yt / xt)
距離(正射影):r = √(xt^2 + yt^2)

仰角:φ = r

距離(立体射影):r' = tan(φt / 2)

X座標(立体射影):x' = r' * cos(θ)
Y座標(立体射影):y' = r' * sin(θ)

4. 立体射影の座標を THETA S 上の座標に変換。

f:id:poipoides:20151209165342g:plain

最後に、THETA S の画像に合うようにサイズや位置、向きなどを調整します。

ココらへんは単純な位置とスケールと角度の調整なので説明ははしょります。

締め

ということで、こんな感じでちょっと複雑になってしまいましたが、シェーダを使って THETA S 画像をパノラマ画像に変換できました!

ちなみに、vvvv x86 の方だと DirectX 9 なのでピクセルシェーダーの演算スロット数が少なく、今回の実装を実現できませんでした。

変換式自体にちょっとまわりくどい部分がありそうなので、いつかブラッシュアップして vvvv x86 の方にも対応できればいいなーと思っています。

それでは、今回はこの辺で。

openFrameworks v0.9.0 をブラウザで動かしてみる

どうもこんにちは。

久々のブログ投稿です。今日はoFネタです。

先日(2015年 11月8日)、openFrameworks v0.9.0 が無事正式リリースになりました!

で、リリース文を読んでみたら、気になる1文が載ってるではありませんか。

oF が正式に Emscripten 対応になったのです!なのでWEB上で動くのです!!

Emscripten とは?

Emscripten とは、LLVM -> JavaScript 変換ができるアツいヤローです。なので、C++ -> LLVM -> JavaScript みたいな感じで、C++ で書かれたコードを JS コードに変換できちゃいます。

C++ -> JS の変換が出来て何が嬉しいの?と思う方もいるかもしれませんが、これまでのゲーム系などのC/C++で書かれたコード資産をそのままWebに流用出来たり、asm.js に変換して高速な実行ができたりなんて利点があるらしいです。(詳しくは知らない。)

で、C++ -> JS 変換ということはですよ。 まさに oF をブラウザ上で動かすことができるようになるのですよ!

試してみた

と、いうことで、物は試しとばかりに早速やってみました。 詳しいやり方はこちらの Setup guide に載っているので、皆様ご自分の環境に合わせてトライしてみましょう!

http://openframeworks.cc/download/

ちなみに私は Mac 環境だったので、Mac に入れてみた流れをなんとなく書いていきたいと思います。

また、私がつまずいたポイントも備忘録的に記しておこうと思います。

まずは Emscripten を入れる!

なにはともあれ、まずは Emscripten を入れなければなりません。

Emscriptenの公式ページは以下になります。

http://kripken.github.io/emscripten-site/index.html

Emscripte を入れるための環境を作る。

まずは Emscripten を入れる前に、Emscripten を入れるための環境づくりが必要です。

こちらのページに Emscripten を入れるために必要なモノが書いてあるのでセコセコとインストールします。

http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#platform-notes-installation-instructions-portable-sdk

  • XCode Command Line Tools
  • git
  • cmake
  • node.js
  • python 2.x

もともとWeb系の方であれば、上記の環境もすでにいろいろとインストール済みかもしれません。 自分の環境にすでに上記が入っているかどうかは、以下のように確認ができるようです。

http://kripken.github.io/emscripten-site/docs/building_from_source/toolchain_what_is_needed.html#toolchain-test-which-dependencies-are-installed

つまずきポイント1:cmake の Command LIne 版のインストールについて

cmake のインストールパッケージをそのまま入れると、GUI版のcmakeが入ります。 ただ、そのままだと Command Line 上から使えないため、Emscripten のインストールでエラーが出てしまいます。

Command Line 版の cmake を入れるには

  1. GUI版 cmake を起動する
  2. [Tool] → [How to Install For Command Line Use] を選択
  3. ポップアップで出てきた指示にしたがう。

という手順で行えます。私の場合は一番上の直接パスを通す方法を使いました。

f:id:poipoides:20151111001443p:plain

つまずきポイント2:El Capitan での/usr/bin以下の書き換え

python2 に関しては以下のようにしろと Emscripten のページに書いてあります。

cd /usr/bin
sudo ln python python2
sudo ln ../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 python22.7

ですが、El Capitan だと Rootless というセキュリティ機能が入っているらしく、そのままでは上記コマンドがエラーになってしまいます。

対処法としては以下を参考にしました

http://applech2.com/archives/46435268.html

Emscripten のインストール

必要な環境を構築できたら、Emscripten 自体をダウンロード&インストール!

詳細はこちらに載っているので参照のこと。

http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html

まずはパッケージをダウンロード&解凍し、適当なディレクトリに置きます。

そしてそのディレクトリ内で以下のコマンドを叩きます。

# Fetch the latest registry of available tools.
./emsdk update

# Download and install the latest SDK tools.
./emsdk install latest

# Make the "latest" SDK "active"
./emsdk activate latest

で、Mac/Linux の人は更に以下を叩きます。

# Set the current Emscripten path on Linux/Mac OS X
source ./emsdk_env.sh

これでアクティブにしたEmscriptenにパスが通るらしいです。

インストールは結構時間がかかったので気長に待つといいと思います。

つまずきポイント3:Emscripten のバージョン

実は上述の ./emsdk install latest だと私の場合下記のようなエラーが発生してしまいました。

Command '['make', '-j3']' returned non-zero exit status 2
Installation failed!

私が実行した時の最新バージョンが 1.35.7 だったのですが、どうもこれが不安定だったようです。

下記に示す様に 1.35.0 を入れるようにしたらうまくいきました。

./emsdk update sdk-tag-1.35.0-64bit
./emsdk install sdk-tag-1.35.0-64bit
./emsdk activate sdk-tag-1.35.0-64bit

パスを通す

最後に Emscripten のもろもろのバイナリがあるディレクトリにパスを通します。

ディレクトリは以下になりますので、忘れずに通してください。

(Emscriptenディレクトリ)/emscripten/(Emscripten のバージョン番号)/

oF を Emscripten でビルドする

さて、ようやく Emscripten インストール出来たので、次は oF のソースを Emscripten でビルドしてみたいと思います。

やり方は以下のとおり。

http://openframeworks.cc/setup/emscripten/

まずはビルドしたいプロジェクトのディレクトリに移動し、以下のコマンドを叩きます

emmake make

しばらくするとビルドが完了するので、そのまま動作チェックしたい場合は

emrun --browser chrome bin/(プロジェクト名).html

と実行すると Chrome が立ち上がってブラウザ上で動いているのが確認できます。

また、サーバーにアップしたい場合は、bin/ 以下のファイルをすべてアップロードすれば良い模様です。

取り急ぎ examples/3d/eDPrimitivesExample をビルドしてみた結果、以下のようになりました!

https://www.googledrive.com/host/0B0kSi87phV3Ia015ejhHNlEwRGM/3DPrimitivesExample.html

f:id:poipoides:20151111002239p:plain

おまけ

ちなみに、作ったoFのページを公開するのには、Google Drive を使った以下の方法を参考にしました。 http://ninagreen.hatenablog.com/entry/2015/09/14/030704

簡単にWEBページを公開できてお手軽です!

それでは、またそのうち。

プリクラアプリのこと

こちらの続き

poipoides.hatenablog.com

さて前回からだいぶ空いてしまいましたが、GW明けの気だるさを利用して粛々とブログを更新しようと思います。

前回までは無線調光器のお話でしたが、今回はガラッと変わってiPadアプリを製作したよって話です。

パーティーではねのうわささんという素敵なお花屋さんに入ってもらって、会場を植物で埋め尽くさんばかりに装飾してもらったのですね。

で、ウエディングパーティー定番のフォトブースも植物でつくっていただいたので、これはもう写真に残したい。できるなら全員分!ということでフォトブース用の写真撮影アプリを作ってiPadに入れてフォトブースに置いて簡易プリクラにしてみた訳です。

f:id:poipoides:20150326134448j:plain

ただ、普通に写真を撮るだけだと面白くないので、

  1. ブースで写真を撮影する。
  2. Flickr に写真をアップロード。QRコードでアクセスできる。
  3. 会場の至る所にiPadを仕掛けて、撮影した写真がそちらにもリアルタイムで写真が共有される。

という感じにしてみました。今回はそこの部分のお話。

構成

すでに上で書いたまんまなのですが、写真撮影用iPadから一旦Flickrに上げます。で、閲覧用iPadは10秒に1回Flickrに上げた写真からランダムに1枚取ってきて表示、みたいなことやってます。

図にすると以下の通り。

f:id:poipoides:20150507222531p:plain

とくに難しい事はしていない(というかする余力がなかった)のでアプリ自体はすごくシンプルになっていますが、、、。問題はFlickrでした。。。

ofxFlickr にもてあそばれて。。。

そもそもなんでアップロード先にFlickrを選んだかというと、

  • 写真アップロード・ダウンロード用のAPIが公開されていること
  • サービス自体が有名で誰かがアップロード・ダウンロード用のコードを公開してくれてる可能性が高いもの

という理由からでした。そして思惑通りopenFrameworks用のFlickrアドオンがすでにGithubに公開されてました。

ところが、これが結構古いやつでアップデートもしばらくされておらず、私が落としてきたときにはもう現在のFlickrでは動かず。。。

いろいろ試したり調べたりした結果、どうもFlickr2014年6月からHTTPSじゃないとAPIアクセスできなくしたからだという事が判明

HTTPSに対応したものに変更してなんとかPCからはアクセスできるようになりました。

こちらコード。

github.com

さらに厄災はつづく

で、PCから動くようになったので調子にのってたら、iPadではうまく認証が通らない。。。

これもいろいろ試してみて、ものすごーい時間をつかった結果どうも以下の2つが理由のようで。。。

  • 認証待ちをメインスレッドでやってるので認証チャレンジ中に動作が固まる
  • 認証トークンなどを保存するファイルがiOSのドキュメント保存場所と違うところをさしている

ということで、こちらもiOS向けに認証処理をスレッド化したり保存場所を変えたりした物をかなりガッツリ無理矢理変更をいれて作りました。

github.com

※これ自分の環境でしか試してないのであんまり参考にはしないでね。

教訓

ということで、完全に急がば回れ状態になってしまいました、というお話でした。とはいえ自前で1から作ってても時間かかっただろうし、これで良かったのかな。良かった事にしよう。そうしよう。。。

締め

ということで、パーティー制作物シリーズも今回で終わりです!

次回以降は、なんか作ったときにふんわり更新するくらいのゆるめの運行でいこうと思っています。

それでは。

無線制御調光器のこと 調光回路の通信部のおはなし

こちらの続き。

poipoides.hatenablog.com

今回は通信部分について

前段

通信ですが、ちょろっと前に書いたのですが以下の感じでやっています。

[PC(vvvv)] --UART-- [TWE-Lite(親機)] --ZigBee(無線)-- [TWE-Lite(子機)] --UART-- [Arduino]

ZigBee 部分については TWE-Lite に元から入っているファームウェアがよしなにやってくれるので、自前で実装するのはPC - WTE-Lite間のUART部とTWE-Lite - Arduino間のUART部です。

PC(vvvv) - TWE-Lite(親機) UART通信

ToCoStickについて

今回、TWE-Liteの親機にはToCoStickを使っています。

ToCoStick(トコスティック) - TOCOS-WIRELESS.COM

ToCoStickってのは要は親機設定にしたTWE-LiteとUSB-Serial変換が一緒になってパッケージ化されている製品で、中身については普通のTWE-Liteと変わりありません。

自前で親機設定にしたTWE-Liteを用意してもいいのですが、接続がラクなのでこちらを使っています。

vvvvについて

vvvvっていうのはWindowsで動くビジュアルプログラミング環境です。

vvvv.org

以前はopenFrameworksなどを使うことが多かったのですが、vvvvだとラクに早くプロトタイピングできることが多いので最近はよくこちらをつかってます。

今回はvvvv - TWE-Lite間をUART通信で、しかもTWE-Liteファームウェアの決まりにのっとったフォーマットでデータをやり取りしなければ行けないので、そこらへんのデータ変換部分については光らせ方の演出と一緒にvvvv上で自前で組んでます。

vvvv - TWE-Lite 間通信について

普通のプログラミング言語でいうところの”コード”のことをvvvvでは”パッチ”と呼ぶそうなのですが、以下にvvvvからTWE-Liteを使うためのパッチを何となく載せておきます。

f:id:poipoides:20150413013108p:plain

データを送信するだけならRS232ノードより上だけでいいのですが、今回は前にちょろっと書いたボタンからの入力などもあったのでこういう感じになってます。

ただし、こちらデータのバッファリングに一部不具合あったり、TWE-Liteの機能の一部しか使えないのでこのまま使うのはちょっと厳しいかも。(パーティーの時は突貫で製作したのでなんとかこのまま乗り切りましたが…)

もうすこしちゃんと実装してTWE-Liteのいろんな機能を使えるようにした物を現在作り直しているところなので、そちらは出来次第、公開しようかなと思っています。

TWE-Liteは本当に入力・出力共にいろいろな使い方ができるので、↑がうまく作れたらもっとこういうハードウェア・PC連携のプロトタイピングが楽になるんじゃないかと思っています!

で、今回 vvvv からはこんな感じのフォーマットでデータを送り出しています。

byte 0-1 2-3 4-5 6-7 8-9 A-B C-D E-F
Data DeviceID CmdID UsrCmdID 調光Data0 調光Data1 調光Data2 調光Data3 終端(CRLF)

上記のうち、DeviceIDとCmdIDはTWE-LiteでUART通信をするうえで必須のデータです。

残りのUsrCmdIDと調光Dataについては今回こちらで定義したデータです。

本当はバイナリデータをそのまま送れれば通信量も少なくていいのですが、TWE-LiteのUART通信の都合上、上記をすべて文字列(ASCIIコード)に変換して送り出しています。

なので調光データは0-255の数値を16進数で表現したうえでそれらを文字列にして送っています。ここら辺の数値変換はvvvvの元から用意されているノードで簡単に実現できるのでラクで良いですね。

PC(vvvv)とTWE-Lite(親機)の通信はざっとこんなかんじです。

TWE-Lite(子機) - Arduino UART通信

TWE-Lite(子機)とArduino間の通信ですが、前述の通り調光データが00 - FFの16進文字列で送られてきます。こちらを解読して元の数値に戻してあげなくてはなりません。

もしかしたら Arduino の標準でそういう変換が楽にできる関数が用意されているカモ知れませんがよくわからなかったのでエイヤっと自前で実装しています。

github.com

#define  MAX_COUNT     (20)    // 1 / 50Hz / 2 / 500us

#define  BYTE2TIM(b)  (MAX_COUNT - ((b) * MAX_COUNT / 0xFF))

(中略)

byte chex2dec(char c)
{
  if (('0' <= c) && (c <= '9')) {
    return c - '0';
    
  } else if (('a' <= c) && (c <= 'f')) {
    return c - 'a' + 10;
    
  } else if (('A' <= c) && (c <= 'F')) {
    return c - 'A' + 10;
  }
  
  return 0;
}

byte hex2dec(char buff[])
{
  return (chex2dec(buff[0]) << 4) + chex2dec(buff[1]);
}

(中略)

void loop() {
  digitalWrite(DEBUG_PIN_1, HIGH);
  // put your main code here, to run repeatedly:
  if (Serial.available()) {
    char c = Serial.read();
    
    if (c != '\r') {
      if (c == '\n') {
        // Do
        if (index >= 5) {
          T_SERIAL_CMD_HEADER header;
          header.devID = hex2dec(&buff[1]);
          header.cmdID = hex2dec(&buff[3]);
          
          if (index >= 10) {
            header.usrCmdID = hex2dec(&buff[5]);
    
            if (header.cmdID == 1) {
              if (header.usrCmdID == 1) {
                byte data01 = hex2dec(&buff[7]); 
                byte data02 = hex2dec(&buff[9]);
                byte data03 = hex2dec(&buff[11]);
                byte data04 = hex2dec(&buff[13]);
                            
                param[0].waitTim = BYTE2TIM(data01);
                param[1].waitTim = BYTE2TIM(data02);
                param[2].waitTim = BYTE2TIM(data03);
                param[3].waitTim = BYTE2TIM(data04);
                
                Serial.println(data01, HEX);
              }
            }
          }
        }
        
        // ~~~~~
        index = 0;
        
      } else {
        buff[index] = c;
        index++;
        if (index >= sizeof(buff)) {
          index = 0;
        }
      }
    }
  }
  digitalWrite(DEBUG_PIN_1, LOW);
}

基本的には

  1. loop関数の中でシリアル通信を監視
  2. CRLFを受信したら受信データを解析。
  3. コマンドID等が合っていれば調光データを文字列から数値にエンコード
  4. 調光データをトライアック制御のためのタイミングデータに計算し直して、各変数に設定

という感じで作っています。

締め

というわけで、通信部分の解説もこれで終わりです。

無線調光器の部分については大体書き終わったので、次回はプリクラシステムの方かなと思っています。

それでは

追記

つづき書いた

poipoides.hatenablog.com