2014年9月26日金曜日

リモコン送信 RaspberryPi編

RaspberryPiの送信側はAVRと共通化出来そうな部分があまりないので、以前からあるFormatAEHA,FormatNEC,FormatSONYはそのままで変更せずにFormatOtherのみ最新のフォーマットに合わせて変更しました。CodeExpandOther()の部分になります。
最初にコードをheader部、PatternTable部、Code部に分けて処理しています。
Code部をビットパターンとそれ以外の処理に分けて、ビットパターンの場合1ビットずつ取り出して0/1High区間、Low区間の長さ、それ以外の場合はPatternTableからHigh区間、Low区間の長さを取り出しHigh区間の間は1CLK毎にHigh/Lowを繰り返し、Low区間の間はLowを繰り返します。
High区間のところをHigh/Lowを繰り返しているのは赤外線LED38KHzのサブキャリアに載せた形で出力されるようにするためです。(76KHzPCM audio I/Fから出力しているためHigh/Lowを繰り返すと38KHzの波形になる)
これらの処理をCode部が終わるまで繰り返しPCMBufferに書き出します。
他のフォーマットは以前のままなので変更なしです。
それぞれのフォーマットのコードをPCMのデータに変換したあと、PCMWrite()をリピート回数分繰り返しPCM audio I/Fからの出力で赤外線LEDをドライブします。


これらのコードはここにあります。

2014年9月19日金曜日

リモコン送信 AVRマイコン編

赤外線リモコンは30kHz~40kHz程度でduty1/2~1/3くらいの副搬送波にデータを載せて送信しています。リモコン受光部はこの副搬送波でフィルターしたデータを取り出して出力してきますが、フィルターは結構広い周波数を許容してくれます。またdutyはフィルターにはあまり関係無いので今回は発光強度を上げるためにも1/2で発光させるようにしています。
AVRでの赤外線出力は8bit TimerであるTIMER0を38kHzでPWM出力し、これをOnしている期間をHighのデータ期間、Offしている期間をLowの期間として使用します。

今回はFormatAEHA,FormatNEC,FormatSONY,FormatOtherの4パターンをそれぞれの処理ルーチンで実装していますが、基本的な動作はHigh・Lowの切り替えタイミングを順次Timer割り込みで処理していくことでリモコン信号を作っています。

例としてFormatOtherの場合を解説します。
IRSendコマンドを受けるとデータが揃うのを待ってIrOutput::Send()が呼び出されます。
この中でFormatの種類を判別してSendOther()に分岐します。
SendOther()ではHeader部分を解釈し内部シーケンス用の変数を初期化してSendOtherBody()を呼び出します。
SendOtherBody()ではデータ領域を1byteずつ順番に読みだして、ビットパターンとそれ以外のパターンに分けます。
ビットパターンの場合は後続するデータを1bitずつ読みだし値によってPattern0/Pattern1のHigh/Low期間のパターンを送出します。
それ以外のパターンの場合、パターン番号のパターンのHigh/Low期間のパターンを送出します。
これをデータの最後まで繰り返し、1セットのリモコンコードを送ります。

その後、リピート間隔分の時間を置いてリピート回数に応じて同じ処理を繰り返し、最後にコマンドの応答を返して終了します。

前回と同じものですがこれらを実装したHA2moduleのコードはここにあります。


2014年9月12日金曜日

リモコン受信 RaspberryPi編

AVRの方のFormatOtherを変更したので、RaspberryPiの方も変更をかけました。
以前はIRRemote.ccの中で処理を完結させていたのですが、赤外線リモコンを受信した後の解釈部分はAVRと共通化しました。
IrAnalyze.cc,IrDataBuffer.cc,IrPatternTable.ccをそのまま持ってきてIRRemote.ccの中でPCM audio I/Fからバッファに赤外線リモコンの生データを読み込んだ後、PulseRecord()の中でAVRのPinChange割込みの処理と等価になるように処理します。
その後はAVRの通常Threadと同じように各フォーマットの解析をしてCodeBufferにリモコンコードを生成しています。
以下詳細。

Record()が記録部分のメイン処理です。
この中のPCMRead()で赤外線リモコンデータを76kHzでPCMAudio I/Fを使ってサンプリングしています。その後、PulseRecord()の中で圧縮処理をしています。
ここまででFormatOtherのデータが生成されている状態になります。
次にFormatAEHA,FormatNEC,FormatSONYのそれぞれの解析をしてみてエラーが出るかどうかを確認しています。どれかでエラーが出なかった場合はそのフォーマット、3つともエラーの場合はFormatOtherとしてファイルに記録して終わります。

PCMRead()の中では、76kHzのサンプリングクロックでPCM Audio Inputのポートに繋いだ赤外線受光部からの信号を記録し32bit毎にデータとして取り込んでいきます。
最初に何らかのデータが来るまで待って、データが来始めたところからサンプリングバッファに記録していきます。このルーチンの中では取り敢えずサンプリングバッファが一杯になるまで記録して戻ります。

PulseRecord()の中ではPCMRead()で記録したサンプリングデータの中の有効な部分をまず切り出します。
データの変化のある最初の部分から連続して100ms以上データがない状態になるまでの期間を有効なデータとしています。
その後、有効な区間を解析していきます。
有効期間を先頭から順番にデータの変化点の区間をHigh,LowのペアのデータとしてSearchPatternTable()、CalcAverage()で (AVRと共通なIrPatternTable.ccのルーチン)PatternTableに登録していきます。
パターンが全て登録し終わると最後のHigh区間のデータを登録しMakePatternConvTable()を呼び出し、登録されているパターンの上位2つのデータを0/1として選び、それ以外のデータを登録順に登録したパターンテーブルを作成します。

その後、再度有効な部分を順番に解析していきます。
データ区間のHigh/Low期間をパターンテーブルから検索し、0/1の場合はビットパターンとして記録、2番目以降の場合はそれ以外のパターンとしてパターン番号を記録していきます。
最後にCodeBufferの先頭部分にデータ長やフォーマットコード、データサイズ、パターンテーブルサイズを記録して戻ります。

以上がRaspberryPiでの赤外線リモコンの記録シーケンスになります。
これらのコードはここにあります。

2014年9月5日金曜日

リモコン受信 AVRマイコン編 その2

前回のTimeout割込みで最終bitの処理をして通常Threadをkickしたところからの続きです。

□通常Threadの処理
FIFOに64パルス分残っているのでこれの記録処理を行います。
次にデータのリピートを確認します。ここでは同じパターンが4回以上連続しているもの、2回、3回連続で丁度データが終わっているものをリピートしているとみなし1回分のデータに短縮します。
これは、HA2moduleで受信したコードをHA1ControlServerに転送し判別して何か動作させるためにコードが同じであることを比較しなくてはならず、リピートも回数の不一致を無視する必要があるので比較しやすいようにこのようなことをしています。最後にデータの先頭にHeaderを書き込み、これでFormatOtherとしてのデータが完成します。
フォーマットは上記の理由でそのままではないですがこちらの電子牛乳さんを参考にしています。
FormatOther
 code[0],code[1]    = length
 code[2]            = FormatOther 0xff
 code[3]            = DataSize
 code[4]            = RepeatCount/PatternTableSize
 code[5]-           = Data .....
    Data:
     0xxxxxxxx(続くbit patternのbit数-1) bit pattern + padding(byte align)
     1xxxxxxxx(PatternTableの番号) : 上位2つ以外のパターンを指定
 code[5+DataSize]-  = PatternTable
    PatternTable:
      [pattern 0] [pattern 1] ... [pattern N]
        [pattern x] : xx xx yy yy (4bytes) : xxxx=HiCLK / yyyy=LoCLK

その後Leader,Dataをチェックし、AEHAフォーマットなら以下のコードに置きまえます。
FormatAEHA
 code[0],code[1]    = length
 code[2]            = FormatAEHA 0x01
 code[3]            = TWIDTH
 code[4] -          = CustomerCode/Parity/Data0, Data1, Data2, ... DataN

NECフォーマットなら以下のコードに置きまえます。
FormatNEC
 code[0],code[1]    = length
 code[2]            = FormatNEC 0x02
 code[3]            = TWIDTH
 code[4] - code[7]  = CustomerCode, Data, ~Data

SONYフォーマットなら以下のコードに置き換えます。
FormatSONY
 code[0],code[1]    = length
 code[2]            = FormatSONY 0x03
 code[3]            = TWIDTH
 code[4]            = AddrLen (5/8/13)
 code[5]            = Data
 code[6],code[7]    = Address

どれでもなければOtherFormatのままXBeeのパケットとして送信します。

□データの記録結果
これで特に長いと評判のダイキンエアコンの電源ボタンをおした時のコードを記録してみると
00 49 ff 30 05 04 00 82 83 3f 11 da 27 00 c5 00
00 d7 84 83 3f 11 da 27 00 42 00 00 54 84 83 7f
11 da 27 00 00 38 32 00 a0 0f 00 06 60 00 01 c0
17 00 00 52 84 00 26 00 1e 00 25 00 61 00 27 07
bc 01 11 00 82 00 27 0a 9e
となりました。
FormatOtherで全体で0x0049=73バイト、データ0x30=48バイト、パターンテーブル5個
PatternTableは後半部分High/Lowの表記で
0026/001e
0025/0061
0027/07bc
0111/0082
0027/0a9e
の5つです。先頭から2つがbit = 0, bit = 1を表すパターンになります。

最初にbit 0 x 5 + Stop





04 00 82 でbit 0(0026/001e) x 5 + Stop(0027/07bc)
 04は続く(4+1)bitがデータパターンであることを示しています。ここでは5bitとも0です。
 82は最上位bitが1なのでパターンテーブルの2番目を示しています。
少し空いてLeader+Data 64bit+Stop




83 3f 11 da 27 00 c5 00 00 d7 84 でLeader(0111/0082) + Data 64bit + Stop(0027/0a9e)
 83はパターンテーブル3番目
 3fは続く(0x3f + 1 = 64(10進数))bitがデータパターン
 84はパターンテーブル4番目を示しています。
少し空いてLeader+Data 64bit+Stop
83 3f 11 da 27 00 42 00 00 54 84 でLeader(0111/0082) + Data 64bit + Stop(0027/0a9e)
少し空いてLeader+Data 152bit+Stop
83 7f 11 da 27 00 00 38 32 00 a0 0f 00 06 60 00 01 c0 でLeader(0111/0082) + Data 128bit
 83はパターンテーブル3番目
 7fは続く(0x7f + 1 = 128)bitがデータパターン
17 00 00 52 84 でData 24bit + Stop(0027/0a9e)
 17は続く(0x17 + 1 = 24)bitがデータパターン
 (ここでデータパターンが2つに分かれているのは1つ目が最上位bitを0にした7bitで表せる最長の値0x7fまでのデータで一度区切っているためです。データパターン長を1引いた値で管理しているのは最長の0x7fの時に丁度16byteで収まる128bitにするためです。1引かずに表してしまうと1つ目が127bitで2つ目が25bitになってしまい1byteデータが伸びてしまいます。)
というフォーマットでした。
波形と見比べてみると合っていそうです。

これらを実装したHA2moduleのコードはここにあります。