Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2014-05-17

Eagleを使った回路設計/基板作成のワークショップに参加してきました

FabLab鎌倉にて開催された『Rapid Prototyping Lesson03 : Circuit Design』という回路設計/基板切削のワークショップに参加してきました。


Eagle という CAD ソフトを使って回路図データをつくり、「MODELA」という切削加工マシンで基板を作成、はんだ付けしてプログラムを書き込むところまでを体験できるワークショップです。


きっかけ

最近 WHILLMoff といったハードウェアスタートアップをお手伝いしてるのですが、iOSエンジニアとしての立ち位置 ではあるので、なかなか回路設計まで自分でやる機会はありません。


とはいえ「回路設計ってどんなもんなのか」というのを一度体験してみたいとは思っていたので、こんなレアなワークショップはなかなかないかも、と参加した次第です。



以下、ワークショップでやった内容です。(+Eagleの使い方のメモなど)


Eagleを使って回路設計

パーツを置いていって(add, copy)、名前をつけて (label, name)、結線して (net) こういう回路図をつくりました。


f:id:shu223:20140517171953j:image:w600


回路部品のライブラリや、回路図はあらかじめ用意されたもの。



で、実際の基板を設計します。


f:id:shu223:20140517172106j:image:w600


route っていうコマンドで配線していくのですが、当然のことながら線同士が重なってはいけないので、部品を move コマンドで動かしたり回したりしつつ試行錯誤するのですが、これがコツをつかんでくるとなかなか楽しい作業でした。


「堤」って大きく書いてあるように、text コマンドで文字列も入れられます。


基板切削

MODELA で基板を切削します。


こんなのが出来上がりました。


f:id:shu223:20140517172137j:image:w500


自分で結線して、名前を入れたら愛着湧きまくりです。


はんだ付け

コンデンサ、抵抗、マイコン、電池ボックス、LEDをはんだ付けします。


f:id:shu223:20140517172237j:image:w500


プログラミング

Arduino UNO を書き込み器にしてプログラムを書き込みます。


とりあえずLチカの定番、Blink を書き込みました。


f:id:shu223:20140517172429j:image:w500


以上で出来上がり!



シンプルな基板ですが、マイコンがついてるので、余ってるピンに他のセンサやらアクチュエータやらつなげてプログラム書き込めばいろんなことができます。


Eagleの使い方メモ

以下作業しながらメモったもの。


※人に伝えるように書いたものではなく、内容の精査もしてないのでそのあたり差し引いてご参照ください。

新規回路図作成
  • 新規プロジェクト作成
  • 新規設計図(schemetic)生成
部品配置
  • コントロールパネル の [Libraries] > [use none] 選択(既存ライブラリの部品を使わないので)
  • useコマンドでライブラリを読み込む
  • addで部品を追加する(さっき読み込んだライブラリの部品も出てくる)
    • CAPA(キャパシティ)を置く
    • 置いたらesc
    • CR2032(電池ボックス)
    • Tiny138p3
    • 回転は2本指タップ
  • deleteで部品を消せる
  • move
  • copy
  • 全部いっぺんにうごかしたいときは group で選択して move 、右クリックで move group
結線
  • 線に名前をふる
    • label
    • name
    • 同じ名前をつけると、接続するかのダイアログがでる
  • 抵抗値をふる
    • value
    • 単位も入れる
      • 1uF
      • オームは opt + z で出てくる
      • (デフォルトがオームなので、入れなくてもよい)
  • ERC(Electric Rule Check)
    • エラーあるとダイアログが出る
    • うまくいくとダイアログでない(下部にNo errors/warningsって出る)
基板設計
  • 左上のほうにある [Switch to Board] ボタンをおす
  • ワークサイズを決める
    • gridコマンド
      • display onにする
      • size10mmにする
    • moveコマンド
      • 50x50の正方形にする(5マスx5マス)
    • gridコマンドでdefaultに戻す
      • 0.5inchごとのグリッドに戻る
    • この枠の中にあるものをイメージデータとして出力して切削することになる
  • moveでパーツを枠内に移す
  • mirrorで緑のパーツは反転させる
    • 実際は2列になってるの以外は変わらない
  • ratsnest
  • route
    • ripupで消す
    • moveで回転させつつ線がかぶらないようにする
      • 電池ボックスの白い線のとこはまたがってもよい
  • DRC(Design Rule Check)
    • Clearance
      • ドリルが通るかのチェック
      • 8milを0.4mmに変える
      • エラーがあればダイアログが出る
      • なければ下部にNo errors
  • 文字を入れる
    • textコマンド
  • polygon
    • 48 Document レイヤーを選択(使わないのでいつもこれを使ってる)
エクスポート
  • export(top)
    • displayでいったんnone選択 -> top, padsを選択
    • image
    • 800dpi
    • monochromeにチェック
  • displayでpads, dimentionを選択してまたエクスポート(holes)
  • displayでdocumentを選択してエクスポート(outline)

top, holes, outlineの3つのpngファイルができる


Arduinoからのプログラム書き込みのメモ

  • Arduinoで追加ハードウェアを認識させる
    • スケッチブックのフォルダ配下にhardwareフォルダをつくり、attinyフォルダごと置く
    • これでArduinoがattinyを認識できるようになる
    • attiny13が今回のボード
  • 配線
  • Arduinoを書き込み器にする
    • Arduinoの [File] -> [Example] > [ArduinoISP]
    • [Tool] -> [Board] -> [Arduino UNO]選択
    • [Tool] -> [Serial Port] USBポートを選択
    • 書き込むと、ArduinoUNOが書き込み器に変わる
  • 書き込む
    • [Tool] -> [Board] -> [ATTINY13] 選択
    • [Tool] -> [Programmer] -> [Arduino ISP]
    • [Tool] -> [Burn Bootloader]
    • エラーが出なければ書き込み準備完了

その他メモ

  • Eagleのライセンス
    • フリー版では10cm x 10cmまでの基盤をつくれる
  • Eagleの独自ライブラリを作成する
    • 既存ライブラリを開くと編集画面になる
    • ライブラリは パッケージ デバイス シンボルの3つにわかれている
    • デバイス 外装やレイアウトを定義するもの
    • シンボル 回路図用のファイル
    • パッケージ デバイスとシンボルを関連づける
  • 基板設計ソフト、KiCad ていうのを使う人も多い 有料

所感

my基板は我が子のようにかわいいです。プログラムや電子回路の知識は一切必要なかったので、ハードをつくる予定がなくてもとりあえず体験してみるのおすすめです。


次回 Lessone04 も同様に「Eagleで回路設計 〜 基板切削」とのことなので、興味のある方はぜひ参加してみると良いかと思います!


Rapid Prototyping Lesson04 : Circuit Design [Actuator] | Peatix


2014-05-10

Core Bluetooth のラッパーライブラリ『LGBluetooth』の使い方

Core Bluetooth はそれほど規模の大きいフレームワークではないので、最初は全容を把握するためにライブラリに頼らずそのまま使ってみるのがおすすめなのですが、ペリフェラルのスキャンやコネクト時のタイムアウト処理等、結局毎回書く必要があって面倒だなーと思う部分もあります。


そのあたりいい感じに書かれているOSSがないかなとGitHub検索してみたところ、次の2つが良さそうでした。


どちらも block-based を売りにしています。


ヘッダだけ見ると YmsCoreBluetooth の方がペリフェラルの保存まで考慮されていて高機能そうな気もしたのですが、どっちも試すのは面倒なので、

  • 更新日が新しい
  • スター数が多い
  • タイムアウト処理が実装されている

という理由からまず LGBluetooth の方だけ試してみました。


ペリフェラルをスキャンする

LGCentralManager の `scanForPeripheralsByInterval:services:options:completion:` メソッドでスキャンを開始します。第1引数に スキャンする時間 [秒] を NSUInteger で指定できるようになっています。

[[LGCentralManager sharedInstance] scanForPeripheralsByInterval:5
                                                       services:services
                                                        options:options
                                                     completion:^(NSArray *peripherals) {
                                                         
                                                         // スキャン完了後の処理
                                                     }];

が、この処理、ソースコードを読んでみると、タイムアウトというよりは、指定時間が経過したら completion ブロックを実行するという実装になっていました。つまり、たとえば scanInterval に10秒を指定したらその間にいくつペリフェラルが見つかっても完了ブロックは実行されません。


これじゃあ使い物にならない、ということで、次のプロパティを追加して Pull Request を送りました。

@property (assign, nonatomic) NSUInteger peripheralsCountToStop;

たとえば下記のように1をセットしておくと、scanInterval 経過前でも、ペリフェラルが1つ見つかった時点で stopScan してスキャン完了ブロックが呼ばれます。

[[LGCentralManager sharedInstance] setPeripheralsCountToStop:1];

既に Merge されています


ペリフェラルに接続する

[peripheral connectWithCompletion:^(NSError *error) {

	// 完了処理
}];

サービスを探索する

[peripheral discoverServicesWithCompletion:^(NSArray *services, NSError *error) {
    
    // 完了処理
}];

キャラクタリスティックを探索する

[service discoverCharacteristicsWithCompletion:^(NSArray *characteristics, NSError *error) {

    // 完了処理    
}];

データをwriteする

[self.characteristic writeValue:data
                     completion:^(NSError *error) {
                         
                         // write完了後の処理
                     }];

接続が切れたときに処理を行う

CBCentralManagerDelegate は内包されてるので、たとえば切断時の処理とかはどうするかというと、 `centralManager:didDisconnectPeripheral:error:` のタイミングで `kLGPeripheralDidDisconnect` という名前の通知が発行されるので、それを監視しておきます。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(peripheralDidDisconnect:)
                                             name:kLGPeripheralDidDisconnect
                                           object:nil];
- (void)peripheralDidDisconnect:(NSNotification *)notification {
    
    // 切断時の処理
}

ログを黙らせる

デフォルトのままだと、デバッグビルドでは値がとんでくるたびにログをはくようになっています。


これを黙らせるには、Podfileで次のように記述します。


pod 'LGBluetooth', :git => 'https://github.com/SocialObjects-Software/LGBluetooth.git'

# インストール後に実行される処理を記述
post_install do |installer|

  POD_TARGET_NAME = "Pods-LGBluetooth"

  # 変更したいビルドターゲットを探す
  classy_pods_target = installer.project.targets.find{ |target| target.name == POD_TARGET_NAME }
  unless classy_pods_target
    raise ::Pod::Informative, "Failed to find '" << POD_TARGET_NAME << "' target"
  end

  # ビルド設定を追加
  classy_pods_target.build_configurations.each do |config|
    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'LG_BLE_SILENCE'
  end

end

上記はCocoaPods使用時ですが、ソースを直接プロジェクトに追加して使用する場合は次のようにpchファイル等で定義するか、

#define LG_BLE_SILENCE

ビルド設定の [Preprocessor Macros] に "LG_BLE_SILENCE" を追加すればOKです。


参考記事:


所感

以上のように API はわかりやすいし、ソースも読みやすいし、プルリクにもすぐに対応してくれるので、結構いい感じです。



2014-05-06

Audio Unit 再入門

Core Audio においてもっとも低レベルに位置する Audio Unit。リアルタイムで高度なオーディオ波形処理を行いたい場合や複雑なルーティングによるオーディオ処理を実現したい場合、これを使用する必要が出てきます。


が、このフレームワーク、個人的には使用頻度があまり高くない *1 ので、ひさびさに触ってみた際にとっつきにくさを感じました。


慣れてしまえば 全体的なコンセプトはシンプル なのですが、関数の引数がやたら多かったり、構造体の要素がやたら多かったり、慣れてないC言語APIだったりするので、久しぶりに触るとそのあたりが複雑に感じてしまうのかなと。


そんなわけで、次に久しぶりに Audio Unit をいじるときに、 そのあたりの「シンプルな全体感」と、「複雑に感じてしまう部分」を切り分けて見ることができるよう、メモっておきます。


基本的な考え方

Audio Unit の基本コンセプトは、「いろいろなユニットを複数接続し、オーディオ処理を実現する」というもの。


で、そのひとつひとつのユニットがノード(AUNode)、それらが繋がっている全体がグラフ(AUGraph)。この考え方や呼称は直観的にもわかりやすいです。


そして実際の実装手順としても、大まかにいうと下記のように非常にわかりやすいです。この流れさえ把握しておけば、後述する「一見複雑そう」な諸々が出てきても全体感は見失わずにすむかと。


基本的な実装の流れ

1. グラフ(AUGraph)作成

NewAUGraph(&graph);
AUGraphOpen(graph);

2. ノードをグラフに追加する

AUGraphAddNode(graph,
               &ioUnitDescription,
               &ioNode);

3. ノードを接続する

AUGraphConnectNodeInput(graph, ioNode, 1, ioNode, 0);

4. グラフを初期化して処理開始

AUGraphInitialize(graph);
AUGraphStart(graph);


で、これに、下記要素が絡んできます。

  • AudioComponentDescription
  • Audio Unit のプロパティ設定
  • AudioStreamBasicDescription (ASBD)
  • コールバック
  • Audio Converter Sevices や Extended Audio File Services 等の関連サービス

このあたりが絡んでくることによって、コードも長く複雑になり、オーディオフォーマットC言語の知識も必要になってきて、そのあたりの知識の乏しい自分が久々に Audio Unit に触るとうわー難しいってなるのかなと。そんなわけで、整理のため以下にひとつひとつ紐解いておきます。


AudioComponentDescription

前述した基本手順の「ノードをグラフに追加する」手順において、 「どのユニットをノードとして追加するか」 を指定するものが AudioComponentDescription という構造体。


たとえば Remote IO ユニットの場合は下記のようになります。

AudioComponentDescription cd;
cd.componentType            = kAudioUnitType_Output;
cd.componentSubType         = kAudioUnitSubType_RemoteIO;
cd.componentManufacturer    = kAudioUnitManufacturer_Apple;
cd.componentFlags           = 0;
cd.componentFlagsMask       = 0;

AUGraphAddNode(graph, &cd, &ioNode);

こうやって書いてしまうと全然難しいものではないのですが、構造体の要素の数が多く、各定義名も長いので、パッと見のコードの複雑さを助長している気がします。


そんなわけで TTMAudioUnitHelper という Audio Unit のヘルパーライブラリでは、下記のように サブタイプだけ指定すれば AudioComponentDescription を取得できる ラッパーメソッドを用意してあります *2

+ (AudioComponentDescription)audioComponentDescriptionForSubType:(OSType)subType;

Audio Unit のプロパティ設定

グラフに追加した各ノード(のユニット)にプロパティをセットするには、まずノードから AudioUnit 構造体を取得します。

AudioUnit ioUnit;
AUGraphNodeInfo(graph, ioNode, &cd, &ioUnit);

で、取得した AudioUnit 構造体を引数に渡しつつ、プロパティをセットします。

AudioUnitSetProperty(ioUnit,
                     kAudioOutputUnitProperty_EnableIO,
                     kAudioUnitScope_Input,
                     1,
                     &flag,
                     sizeof(flag));

引数が多いですが、関数の定義を見ればわかるかと。

AudioUnitSetProperty(AudioUnit              inUnit,
                    AudioUnitPropertyID     inID,
                    AudioUnitScope          inScope,
                    AudioUnitElement        inElement,
                    const void *            inData,
                    UInt32                  inDataSize)

プロパティのgetも同様です。

AudioUnitGetProperty(AudioUnit              inUnit,
                    AudioUnitPropertyID     inID,
                    AudioUnitScope          inScope,
                    AudioUnitElement        inElement,
                    void *                  outData,
                    UInt32 *                ioDataSize);

プロパティを set / get する実装は、引数が多いので何となく難しい感じに見えてしまうところがありますが、上記の通りやってることはシンプルです。


AudioStreamBasicDescription (ASBD)

オーディオデータフォーマットを表現するための構造体。

struct AudioStreamBasicDescription
{
    Float64 mSampleRate;
    UInt32  mFormatID;
    UInt32  mFormatFlags;
    UInt32  mBytesPerPacket;
    UInt32  mFramesPerPacket;
    UInt32  mBytesPerFrame;
    UInt32  mChannelsPerFrame;
    UInt32  mBitsPerChannel;
    UInt32  mReserved;
};
typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;

ユニットごとにサポートしているオーディオデータフォーマットが違うため、ノード間で滞りなくオーディオデータを流すためにこれをプロパティからセットしてやる必要があります。


この ASBD、以下の点を個人的に整理できてないので、また別途記事を書こうと思っています。

  • どのノード間において明示的に get / set する必要があるのか
  • どのノード間において AUConverter ユニットや後述する Audio Converter Sevices で ASBD を変換する必要があるのか
  • ASBD が合ってなければ AUGraphInitialize 実行時にエラーを返してくれる?

コールバック

再生するにしても録音するにしてもこのコールバックの実装は不可欠だし、リアルタイム波形処理もここで行うことになるので、Audio Unit のキモになる部分といえます。が、下記理由により個人的にはややこしく感じてしまいます。

  • いろいろなコールバックの登録方法がある
  • いろいろなコールバックの種類がある
    • 後述する Audio Converter Sevices もコールバック内で処理を行う
  • 引数それぞれの役割を把握してないとAudioUnitのキモである波形処理を書けない
  • C言語的な知識がしっかりと要求される

ここでは、コールバックの何がややこしいのか、という把握だけにとどめておいて、コールバックの詳しい話は別記事で行いたいと思います。


Audio Converter Sevices や Extended Audio File Services 等の関連サービス

サウンドファイルの再生は、AVAudioPlayer を使用すれば恐ろしく簡単にできるのですが、Audio Unit を使う場合、オーディオデータを RemoteIO で再生できるフォーマットに変換するために、Audio Converter Sevices や Extended Audio File Services を利用する必要があります。


高レベルAPIに慣れてしまった僕のようなゆとりiOSエンジニアからすると「たかがファイル再生」と油断しているところに、Audio Converter Sevices では変換用コールバックが再生用とは別途必要だったり、マジッククッキーなるよくわからない概念が登場したりするので、「Audio Unitこわい」という印象を持ってしまう要因のひとつになってしまっている気がします。


また Audio Converter Sevices を使うにしろ Extended Audio File Services を使うにしろ、この手の実装はファイルから読み込んだオーディオデータを保持しておくために独自の構造体を定義して取り扱うことが多く、他人のサンプルコードを参考にしようとしても、「パッと把握しづらい」というつらさもあります。


このあたり、AUConverter ユニットや AUAudioFilePlayer ユニットを使用すればもっとシンプルにできるのかなと期待しつつ、また別途記事を書こうと思います。


参考記事

Audio Unit を含む、iOSのオーディオ処理に役立つ参考書籍を下記記事にまとめています。


Audio Unit のユニット種別は AudioComponentDescription 構造体の componentSubType によって規定されますが、その一覧を下記記事にまとめています。


参考になるサンプルコードのまとめ。使用されているユニットも付記してあるので手前味噌ながら便利です。

*1:自分の場合は1年3ヵ月ぶり2回目

*2:まだ全てのサブタイプをサポートしていませんし、オプションは考慮できていません。pull request大歓迎です。

2014-05-02

Facebook Paper のアニメーションエンジン「pop」のソースコードを読んでみる

先日公開されたFacebookのpop、アニメーションライブラリですよとリリース前から言われてたものの、popについて語られるときは大抵「Paperアプリのテクノロジーをオープンソース化」という枕詞がつくので、Paper風UIコンポーネント(ジェスチャーでヌルヌル操作できる)もライブラリに含まれてたらいいなとか、せめてサンプルが Paper 風だといいなとかうっすら期待してたのですが、まったくそんなことなく、やっぱり純粋なるアニメーションライブラリでした。サンプルプロジェクトすらつかない硬派っぷり。。


じゃあ今まで実現が難しかったような何か(ド派手なアニメーションとか?)ができるようになってるのか、というとそういうわけでもなくて、


In addition to basic static animations, it supports spring and decay dynamic animations, making it useful for building realistic, physics-based interactions.


ベーシックなアニメーション以外には、 spring (バネ風にボヨヨーンとなる)と、 decay (スーッと速度が減衰する) アニメーションをサポートしている、ってことで、正直地味です。


バネっぽいアニメーションに関しては iOS 7 で標準サポートされた し、イージングについてはCore Animation で自由に数式や関数の曲線を指定できるので、このあたりはどうとでもできるし、そのあたりを いい感じにラップしてくれてる OSS もいくつかあります。


そんなわけで pop の神髄はそこ自体ではないはず、この記事 にあるような、パフォーマンスへの配慮 とかカスタマイズ性 にあるはず、で、それって具体的にどういう実装になってるんだろう、とソースを見てみることにしました。


popクラス群の依存関係

`POPVector` とか `TransformationMatrix` とかどうアニメーションに活かされてるのかパッとわからなかったので、 objc_dep でクラスの依存関係を出力 してみました。


f:id:shu223:20140502192405p:image:w600


・・・相当入り組んでますが、局所的にみればこのクラスはこれとあれに参照されてるのか、とかいろいろわかります。


まずはリファクタリング・・・

さて、コードを読んでいくか、と思ったものの、「え、ほんとにこの入り組んだ依存関係、これでいいの?」というのが気になってきて、依存関係の交通整理をしてプルリク送りました。


https://github.com/facebook/pop/pull/57


f:id:shu223:20140507165033p:image:w600

(修正後の依存関係図)


自分および自分のチームだけでメンテしていく分にはこんなこと気にする必要もないと思うのですが、なにぶんオープンソースプロジェクトで、かつ多くの人が手を入れる超有名リポジトリなので、このへん交通整理しとかないと今後もっとカオスになってくるんじゃないかと思いまして。。


自分のコードであればもっと盛大にリファクタリングするのですが、まだ設計思想をしっかり理解できてない(Internalナントカ、Privateナントカはこのライブラリにおいてどういう役割として定義してるのかとか)ので、修正は最小限にしました。


2014.5.8 Merge されました!)


POPSpringAnimation

ばね風アニメーションを司るクラス POPSpringAnimation については、下記記事でアルゴリズムが解説されています。


Entry is not found - Otsuka Fumiyukiのブログ

(コメント欄にてお知らせいただきました!)


POPSpringSolver で加速度、位置、速度を算出しており、その際にベクトル演算が諸々定義された POPVector を参照しています。


使い方の参考例

popを使ったサンプルコードをGitHubで検索していろいろ探してみた中でおもしろかったもの。



(つづく・・・かも・・・)


2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |