作るものの全て

どう記事を進めようか考えていましたが、まずは全部お見せしてから解説をしていくことにします。今回は作って使うための一式で、中身の解説は次回以降です。
というわけで回路図です。

TinyRemoCon 回路図

そしてソースコードと HEXファイルです。

ソースコードと HEXファイル

※2022/05/10 更新しました

回路を組んで HEXファイルを書き込めば、とりあえずボタンテストが動きます。
ソースコードは 4 TAB です。

スペック的なことをいくつか書いておきますと、この TinyRemoCon は ATtiny10 で動作します。電源は 1.5V の乾電池 2本を想定しています。システムクロックは最大 500kHz、赤外線サブキャリアは分周比の都合で 500kHz /13 ≒ 38.46kHz です。本体コードは 440 bytes で、リモコンデータの定義のために 584 bytes 残っています。添付した HEX には 21ボタン分のテスト用データが含まれていて、全体で 950 bytes です。

2022/05/14 に内部方式を変えた新バージョンを公開しています。使い方は本記事で説明しているままで、コードサイズが 378 bytes になりました。「(3)インサイド TynyRemoCon」までの記事は古いバージョンで説明していますが、実際に使う方は新バージョンの方がよいと思います。

ボタンは 2~20+1個まで任意に設定することができます。この +1個というのは回路図上に示されるエクストラボタンで、BTNOUT に直結する位置にあるものです。しかしエクストラボタンは BTN0 と同時に押した場合に BTNOUT が GND にショートしてしまうので、そのようなケースが排除できる場合のみに利用できるボタンです。ボタン数もソースコードで定義する必要がありますが、まずは他の定義も含めて雰囲気を確認するため、data_test.asm を見てみてください。
また、ATtiny10 における多ボタンの実装については以下のシリーズで扱ってるので、参考にしてください。

リモコンに必要な多ボタンの実現についての記事

この記事では回路図上の R の決定方法も説明していますが、以下のように決定してください。

R < 34.28kΩ / ボタンの数(エクストラボタンは数に含めない)

計算値より小さい抵抗であればよいので、ボタンが何個であれ 1kΩ など決め打ちで行っていただいても結構です。これら抵抗は全て直列に接続し、その節目にボタンがあります。ボタンをどのような配置にするかはお好みでよいです。どれが何番のボタンか分かっていれば、それに合わせてボタン定義すればよいのです。

LEDOUT周りは少しだけ特殊に見えるかもしれません。赤外線LED をトランジスタでドライブしていますが、そのベースに可視光の LED を置きました。赤外線LED だけだと寂しいですしデバッグにも支障があるので、やっぱり可視光の LED はあった方がいいですよね。そしてそれをトランジスタのベースに置いているのはエコ意識の高まりによるものです。LED を光らせる電流を GND へ捨てたりせず、トランジスタのベース電流に流用しています。

添付のソースコードには、20+1ボタンとしてアセンブルした HEXファイルを同梱しています。リモコンデータはテストデータで、ボタン認識の動作確認用として Lチカのように見えるデータを定義しています(data_test.asm)。押したボタン番号を 2進数 5bit でチカチカさせます。ただしボタン0 には、ウチの照明の ON/OFFリモコンのデータを入れておきました。押してみればなんとなく赤外線リモコンっぽく光ってるのが分かると思います。

なお念を押しますが、このリモコンは学習リモコンではありません。自分の欲しいリモコンのボタンを寄せ集めてデータを定義してマイコンに書き込んで使います。書き直さない限りはずっとそのままの固定リモコンです。

リモコンデータ定義

赤外線リモコンのボタンには、それが押された時に送信するデータを定義できなければなりません。できるだけ多様なリモコンに対応できるよう、前回の記事でパラメータ化する項目を決めました。それを実際のデータフォーマットに落とし込みました。
※説明文では、正式な呼び名は分かりませんが T の時間をティックと呼んでいます
※TinyRemoCon ではサブキャリア周波数は 38.46kHz で、記載もそのようにします

リモコンデータ定義フォーマット

リモコンデータはボタン毎に次の構成で定義します。

[ヘッダ][コマンド][コマンド][コマンド]…

ヘッダで T の時間や BIT値0、BIT値1 の表現などを定義した後、赤外線を ON や OFF にしたりバイト列やビット列を送信するなどの動作を行うコマンドを任意に列挙します。

ヘッダ

ヘッダは次の 3バイトで構成します。

ヘッダ
I … 反転フラグ  0/1

NEC や家製協フォーマットでは、Bit値出力を ON から開始します。一方で SONYフォーマットでは、Bit値出力を OFF から開始します。

bit値 出力例

反転フラグは、ON と OFF どちらから開始するかを定義します。
0 … ON から開始(NEC/家製協など)
1 … OFF から開始(SONYなど)

TICKLEN … ティック長 7~127

ティック(T)の長さを、38.46kHz サブキャリアのパルス数で定義します。最低処理クロック確保のため、最低値は 7 です。それ未満の値については動作保証しません。
代表的な値としては、NEC: 21、家製協: 16、SONY: 23 です。

BIT0_1STLEN/BIT0_2NDLEN … BIT値0出力 先行/後行ティック数 1~15

Bit値0 出力の ON/OFF 構成。BIT値0 を出力する際、反転フラグ I が 0 ならば ON を BIT0_1STLEN ティック出力後に OFF を BIT0_2NDLEN ティック出力します。
反転フラグ I が 1 ならば OFF を BIT0_1STLEN ティック出力後に ON を BIT0_2NDLEN ティック出力します。

BIT1_1STLEN/BIT1_2NDLEN … BIT値1出力 先行/後行ティック数 1~15

Bit値1 出力の ON/OFF 構成。詳細は Bit値0 出力の際と同じ。

コマンド

コマンドは赤外線の ON/OFF を行う命令群です。コマンドを適切に並べて必要な赤外線出力が得られるようにします。

ON/OFF

赤外線を指定ティック数 ON または OFF にします。リーダーやストップビットの出力や時間待ちへの利用を想定しています。これを定義するマクロは _ON(tick) と _OFF(tick) です。

ON/OFF コマンド
C … 出力フラグ 0/1

0 … 赤外線 ON
1 … 赤外線 OFF

TICKS … 出力ティック数 1~63

赤外線を ON または OFF にする時間をティック数で定義します。

送信終了

赤外線送信を終了します。赤外線出力は OFF になります。これを定義するマクロは _END です。

END コマンド

BYTE 送信

8bit 単位のバイト列を送信します。これを定義するマクロは _BYTE(len) です。

DATA_LEN … バイトデータ長(送信バイト数) 1~63

BYTE送信コマンドに続く送信データのバイト数を定義します。

BYTEn … 送信バイト

送信する 8bit データです。Bit 0 から送信される LSB first です。バイト送信の際の Bit値0 や Bit値1 の構成はヘッダの定義に従います。

BIT 送信

最大 7bit のビット列を送信します。これを定義するマクロは _BIT(len) です。

BIT_LEN … ビットデータ長(送信ビット数) 1~7

BIT送信コマンドに続く 1バイト中のデータ長をビット数で定義します。

BITS … 送信ビット列

送信するビット列で、その長さは BIT_LEN で定義されます。Bit 0 から送信される LSB first です。バイト送信の際の Bit値0 や Bit値1 の構成はヘッダの定義に従います。

リピート制御

ボタンを押し続けている間のリピート動作を定義します。
リピートは同じリモコンコードの送信を繰り返す方法と、リピート専用のリピートフレームを繰り返す方法を想定しています。その制御のために BACK と THRU の 2つのコマンドを使います。

同じコードの繰り返し
リピートフレームの繰り返し

BACK も THRU も赤外線出力を OFF にしてから指定時間待ちます。時間待ち後、BACK は THRU で設定されるリピートポイント(初期値はコマンド群の先頭)に戻ります。THRU はリピートコマンドを設定して次のコマンド以降を実行します。これを定義するマクロは _THRU(t) と _BACK(t) です。

C … コマンド 0/1

0 … BACK 時間待ちをしてからリピートポイントへ戻ります
1 … THRU  時間待ちをしてからリピートポイントを設定し、続くコマンドの実行を継続します

WAIT … 待ち時間 0~15

リピートまでの待ち時間を定義します。実際の待ち時間は (WAIT + 1) * 16T です。待ち時間の最大は 256T となります。必要に応じて OFF 出力などと組み合わせて待ち時間の調整を行います。

リモコンデータの作り方

用意するもの

  • コピー元のリモコン(複数可)
  • PC
  • フォトトランジスタのアレ
  • 波形が見られる録音ソフト
  • AVRアセンブラ

コピー元のリモコン、これはいいですね。これのボタンをコピーするのがそもそもの目的です。私のケースでは、部屋の照明とエアコンのいくつかの運転状態をコピーしたいのでした。

コピー元の照明とエアコンのリモコン

PC も必要です。コピー元のリモコンの信号を分析したり、定義したデータをソースコードと一緒にアセンブルするのに(もちろん ATtiny10 への書き込みにも)必要です。

前回の記事で作ったフォトトランジスタのアレ、これもコピー元のリモコンのコードを読み取るのに必要です。

フォトトランジスタのアレの回路図
フォトトランジスタのアレ 作成例

波形が見られる録音ソフト、これも必要です。コピー元リモコンのコードは、フォトトランジスタのアレを経由して PC のマイク入力から音声として録音します。そしてその波形を見て、リモコンコードを分析するのです。私が使っているのは Windows用の GoldWave というソフトで、制限付きで無料で使い続けることはできますが有料ソフトです。

リモコンコード録音のイメージ

リモコンコードを読み取って作る定義データは、以下のようにアセンブラソースの中のデータ列として定義します。

DATA_LIGHT:
    .db 0x15, 0x11, 0x31, _ON(16), _OFF(8), _BYTE(4), 0x82, 0x6D, 0x1A, 0xE5, _ON(1), _THRU(3), _ON(16), _OFF(8), _ON(1), _BACK(9)

これをプログラム本体と一緒にアセンブルする必要がありますから、AVRアセンブラが必要です。

なお、コピー元のリモコンコードが既に分かっているのであれば録音の必要がありませんから、信号分析に使うものは必要ありません。そのリモコンコードをこの TinyRemoCon の定義フォーマットに沿ってデータ化すればよいです。世には様々なリモコンデータベースがあるので、それを活用しない手はありません。

リモコンコードの録音

以下のように PC のマイク端子に接続したフォトトランジスタのアレにリモコンを向けて、PC の録音ソフトを録音状態にしてからリモコンのボタンを押します。

リモコンコード録音の様子

入力レベルや赤外線リモコンの距離などを調整して見やすい波形になるようにしてください。こんな感じの波形が見られるでしょうか。

リモコンコード録音例

これら 3パターンはオーディオインタフェース違いの録音例ですが、このように PC やオーディオインタフェースによってどのように読めるかは違うようです。何度やっても画像の一番下のようにしか読めない場合、ここからコードを読み取るには無理がありますので残念ながら別のオーディオインタフェースで試す必要があります。
上段や中段のようにデータ的なものが見えたら OK ですが、両者は上下が反転しています。回路的には中段が正解なのですが、これは赤外線が ON の状態で下向きのパルスになる負論理です。感覚的には正論理の方が見やすいと思いますので、負論理で読めた場合は波形編集などで上下反転しておくとよいかもしれません。もちろん負論理であることを意識して読めばそのままでも問題ありません。

エアコンのリモコン

エアコンのリモコンは特殊です。リモコンが次のような状態のとき、

冷房 25℃

温度の ▲ ボタンを押すと次のようになります。

冷房 26℃

冷房の温度が 25℃から 26℃に変化しました。何の疑問も無いですね。
でもこの時に赤外線で送信されるコードは、「温度を上げるボタン」というコードではなく、「冷房 26℃、風量Max、風向きあっち、etc.」という、リモコンが持っている運転状態の情報全てです。

つまりエアコンのリモコンコードについては、ボタンの動きのコピーはできません。特定の運転状態を何パターンかコピーして TinyRemoCon のボタンに例えば「冷房 弱(28℃)」「冷房 中(25℃)」「冷房 強(22℃)」などのように割り当てます。

リモコンコードの分析

まず録音したリモコンコードをざっと眺めて、どのタイプのフォーマットなのかを判断するのが近道です。次の画像は私の部屋の照明の ON/OFF ボタンです。

照明 ON/OFF ボタン

縮尺の関係でこのソフトでは波形が塗りつぶし表示になっていますが波形は分かります。ざっと見ますと、1 の部分がボタンコードのフレーム(以後本体フレームと呼びます)、2 と 3 がリピートフレームです。このリモコンはリピートをリピートフレームで送信していますが、本体フレームを繰り返すものもありますし、繰り返さずワンショットで終わりのものもあります。

このリモコンはリピートする。リピートにはリピートフレームが使われる。
ボタンフレームの終了からリピートフレームは約40ms、リピートフレームの終了から次のリピートフレームまでは約95ms。

次にティック(T)の時間を確かめます。ボタンフレームのパルスを拡大して数周期選んで、そのティック数と時間を測ります(できるだけ広く選んだ方が時間を正確に出せますが、数えるのは大変になります)。

T の測定

パルスの比率を見るに、ON が 1T と OFF が 1T の部分と ON が 1T と OFF が 3T の部分になってそうです。これはそれぞれ Bit値0 と Bit値1 のパターンでしょう。となると、選択範囲は 44T で、この時間は約25ms ですから、1T は約568μs ということになります。

T = 568μs(仮)
Bit値0 = 1T ON + 1T OFF、Bit値1 =1T ON + 3T OFF

続いてリーダーを見ます。

リーダーの測定

ON の時間と OFF の時間をそれぞれ選択して測ってもよいですが、ここではリーダー全体を選択しました。ぱっと見で ON と OFF の比率は 2:1 だろうと思ったからです。リーダー全体は 13ms のようで T は 568μs ですから、リーダー全体で約23T のようです。ちょっと半端ですが、たぶん 16T ON + 8T OFF の 24T でしょう。そもそも選択範囲の時間の表示精度の問題もあって、こんなもんです。

リーダーは 16T ON + 8T OFF

ここまでで、これは NECフォーマットのリモコンであろうことが分かりました。であれば T の時間は 562μs に修整しておきましょう。リモコンの個体差や電池の具合で幅はあります。

このリモコンは NECフォーマット
T = 562μs

ちなみに T の判断を終えた後は、これが NECフォーマットなのかどうかというのは割とどうでもいいです。あとは粛々とデータを読むだけです。

ボディの読み取り

フレームのボディをBit値0 と Bit値1 で白と赤に塗り分けてみました。これは録音ソフトの機能ではありません。mspaint でやりましたが、あくまで説明用の作業ですから分析に当たって色塗りは必要ありません。で、この 0 1 を読んでいきます。8bit区切りにしておくとよいと思います。

01000001 10110110 01011000 10100111

こんな風に読めたでしょうか。画像でも最後に塗り残していますが、パルスがひとつ余っています。これはストップビットです。この本体フレームのボディは 4バイトで構成されているようですね。SONYフォーマットでは 7bit 単位だったりするようですが、それでもここでは 8bit 単位に区切って読みます。TinyRemoCon のデータ定義の単位を 8bit としたためです。ですので端数ビットが数bit 残ると思いますが、それも別途記録しておいてください。

このように 0 1 で読んだビット列は、16進数に直しておきます。TinyRemoCon では LSB first でデータを送信しますから、最初に現れた bit値を LSB に置きます。ですから上記で読み取ったビット列はバイト内で左右反転してデータ化する必要があります。こんな感じです。

01000001 → 10000010 → 0x82
10110110 → 01101101 → 0x6d
01011000 → 00011010 → 0x1a
10100111 → 11100101 → 0xe5

ボディは 0x82 0x6d 0x1a 0xe5、ストップビットは 1T ON

このボタンにはリピートフレームがありましたから、それを見てみます。

リピートフレーム

リーダーとストップビットだけのようですね。

リピートフレームはリーダー + ストップビット、すなわち
16T ON + 8T OFF + 1T ON

以上で私の家の照明の ON/OFF ボタンのリモコンコードの分析が終わりました。これを所定の書式で TinyRemoCon に定義します。

リモコンコードの定義

リモコンコードはアセンブラソースコード上に定義します。冒頭で添付したソースコードでは data_test.asm がそれです。これは data_inc.asm から次のように include されています。

; リモコンデータ定義読み込み
.include "data_test.asm"

自分で新しく定義ファイルを作ったなら、上記部分を自分のファイルを参照するように修整してください。ここでは data_test.asm を使って説明します。

まずはボタン数の定義です。ご自分で作った TinyRemoCon に合わせて定義してください。

; ボタン数 2 ~ 20(エクストラボタン含まず)
.equ BTN_NUM = 20
; エクストラボタン 0:無し 1:あり
.equ BTN_EXTRA = 1

続いてボタンデータのテーブルです。

    ; データアドレステーブル
DATA_PTR:
    .dw ADDR(DATA_SAMPLE)   ;  0    サンプルリモコンデータ
    .dw ADDR(DATA_TEST01)   ;  1    TEST
    .dw ADDR(DATA_TEST02)   ;  2    TEST
    .dw ADDR(DATA_TEST03)   ;  3    TEST
    .dw ADDR(DATA_TEST04)   ;  4    TEST
    .dw ADDR(DATA_TEST05)   ;  5    TEST
    .dw ADDR(DATA_TEST06)   ;  6    TEST
    .dw ADDR(DATA_TEST07)   ;  7    TEST
    .dw ADDR(DATA_TEST08)   ;  8    TEST
    .dw ADDR(DATA_TEST09)   ;  9    TEST
    .dw ADDR(DATA_TEST10)   ; 10    TEST
    .dw ADDR(DATA_TEST11)   ; 11    TEST
    .dw ADDR(DATA_TEST12)   ; 12    TEST
    .dw ADDR(DATA_TEST13)   ; 13    TEST
    .dw ADDR(DATA_TEST14)   ; 14    TEST
    .dw ADDR(DATA_TEST15)   ; 15    TEST
    .dw ADDR(DATA_TEST16)   ; 16    TEST
    .dw ADDR(DATA_TEST17)   ; 17    TEST
    .dw ADDR(DATA_TEST18)   ; 18    TEST
    .dw ADDR(DATA_TEST19)   ; 19    TEST
    .dw ADDR(DATA_TEST20)   ; 20    TEST

DATA_PTR から始まってボタン0 から順にボタンに割り当てるボタン定義のラベルを書いていきます。ボタン数分必要です。無効ボタンが欲しい場合、何もしないボタン定義を作ってそれを参照するとよいです。これは同梱の data_empty.asm を見ると分かると思います。

続いてリモコンデータの定義です。今回読み取ったボタンをリモコンデータ定義フォーマットに則って DATA_LIGHT として定義すると、こんな感じです。.db は必ず 1行で書いてください。

DATA_LIGHT:
    .db 0x15, 0x11, 0x31, _ON(16), _OFF(8), _BYTE(4), 0x82, 0x6D, 0x1A, 0xE5, _ON(1), _THRU(3), _ON(16), _OFF(8), _ON(1), _BACK(9)

.db の先頭 3バイトがヘッダです。最初の 0x15 は 10進数で 21 が、これは 1T の時間をサブキャリア 38.46kHz のパルス数で表した時間です。今回読み取った 1T の時間は 562μs でしたが、これと 38kHz を掛けるとこの値が求まります。

562μs * 38.46kHz = 21.6 パルス

端数が出ますので四捨五入して 22 とするのが妥当かもしれませんが、切り捨てて 21 にしました。マージンもありますし、送信が早く終わる方がエコかと思いまして。
いずれにしてもこれを 21 としたことよって T は 546μs になりますが、時間の計算などでこれを区別する必要がある場合は T’ と書きます。

続く 0x11 は Bit値0 の定義です。今回の Bit値0 は、最初に 1T ON でした。なのでこれを下位 4bit で定義します(0x●1)。次に 1T OFF でしたので、これを上位 4bit で定義します(0x1●)。合わせて 0x11 です。

続く 0x31 は Bit値1 の定義です。今回の Bit値1 は、最初に 1T ON でした。なのでこれを下位 4bit で定義します(0x●1)。次に 3T OFF でしたので、これを上位 4bit で定義します(0x3●)。合わせて 0x31 です。

次にリーダーを定義します。リーダーは 16T ON + 8T OFF でしたので、このまま _ON(16), _OFF(8) です。

その後ボディのデータが 4バイトありましたので、_BYTE(4) に続いてデータ本体を 0x82, 0x6D, 0x1A, 0xE5 と並べます。
もし SONYフォーマットなどでデータがバイト単位に収まらず端数ビットが出た場合、_BIT(n), d のように書きます。n がビット数で、1バイトデータ d の下位ビット側 n bit に端数ビットを置きます。

続く赤外線出力はストップビット 1T ON でしたから、これを _ON(1) として定義します。
リピートを行わないボタンであれば _END で締めくくりますが、このボタンはリピートを行います。リピートまでの時間は約40ms でしたが、1T’ = 546μs ですから約73T’ の時間です。_THRU では 16T’ 単位で待ち時間を定義できるので、64T’ 待つか 80T’ 待つかのどちらかです。ただリピートフレームが遅れると機器がリピート終了と判断しかねないので、64T’ 待つことにします。というわけで本体フレームからリピートフレームまでの間隔を _THRU(3) と定義します(_THRU の待ち時間は (n + 1) * 16T’ です)。

リピート待ちの後はリピートフレームが続きます。リピートフレームにもリーダーが付いていますから _ON(16), _OFF(8) となり、そのままストップビットですから _ON(1) です。リピートフレーム終了から次のリピートフレームの間隔は 95ms で、これは 173T’ になります。なので 160T’ を _BACK(9) で待つことにします。_BACK を使うことにより _THRU 直後に戻り、ボタンが押されている間はリピートフレームが繰り返し送信される動作になります。

以上でこのボタンの定義が完了です。16 bytes の定義データとなりました。
このように分析したボタンのデータ定義が終わったら、DATA_PTR からのアドレステーブルの更新も忘れずに行ってください。これを ボタン2 に割り当てるならこんな感じです。★印の行です。

    ; データアドレステーブル
DATA_PTR:
    .dw ADDR(DATA_SAMPLE)   ;  0    サンプルリモコンデータ
    .dw ADDR(DATA_TEST01)   ;  1    TEST
    .dw ADDR(DATA_LIGHT)    ;  2    照明 ON/OFF ★
    .dw ADDR(DATA_TEST03)   ;  3    TEST
     :

もう一例

これはうちのエアコンの停止ボタンを押した時のリモコンデータです。

エアコン 停止

すごく長いですが、分析の仕方は同じです。リピートはしません。ただよく見ると、2つのフレームで構成されているようです。やはり結構特殊ですね。
この定義はこんな感じになりました。

.db 0x10, 0x11, 0x31, _ON(8), _OFF(4), _BYTE(8), 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, _ON(1), _OFF(23), _ON(8), _OFF(4), _BYTE(19), 0x02, 0x20, 0xE0, 0x04, 0x00, 0x40, 0x2E, 0x80, 0xA4, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x80, 0x00, 0x06, 0x84, _ON(1), _END

照明のリモコンとは、T の時間も違います。照明はサブキャリア 21パルスで定義しましたがこちらは 16パルスです。リーダーの長さも違いますね。2つのフレームの間隔は _OFF(23) で定義しています。そしてリピートしませんから、最後は _END で終わっています。40 bytes の定義データになりました。

続く

実際に使用できるよう、定義フォーマットの仕様や定義の方法に終始したので退屈な内容だったかもしれません。ただこれを乗り越えないと使用することはできないので、心を鬼にしました。計算も入って面倒だと思いますが、やっていることは単純なので慣れればあっさりできると思います。ON や OFF したりバイトデータやビットデータを送るだけですから。
もちろんリモコンの信号の分析など全部人手でやる必要は無く、こういうのは分析プログラムでも作って自動化するべきところです。一応私も分析補助スクリプトを組んで使っていますが、使い方が特殊過ぎて公開できる状態ではありません。整理できたら公開するかもしれませんが、あまり期待しないでください。
次回は中身の解説の予定です。あえて 500kHz という低めなシステムクロックを選んだので、アセンブラでクロックを数えながらゴリゴリやります。

このシリーズ

この記事です

関連記事

リモコンに必要な多ボタンの実現についての記事