たくさんの自由帳

この記事の赤外線パターン解説編?

変換用Webページ出来ました

多分使えます。

https://nec-ir-pattern-hex.netlify.app/

ソースコード(Kotlin/JS製Webアプリ):https://github.com/takusan23/IRPatternHex

Imgur

赤外線のパターンを16進数に変換する

スタートはこれ

[8925, 4540, 514, 626, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 626, 514]

そしてゴールはこれです。

0x41b67d82

調べていると赤外線のパターンではなく、データは 0x~ です!みたいなのを見かけたので調査。

合っているかは知りません。

参考にしました:

http://shrkn65.nobody.jp/remocon/nec.html
http://elm-chan.org/docs/ir_format.html
https://qiita.com/katsumin/items/517cd9fbe66e26d72fa7
https://god-support.blogspot.com/2020/08/nec.html

やってること

  • 赤外線パターンからどこのフォーマットを使っているかを調べる。
  • フォーマットの仕様に沿って、データ部(後述)を0と1に変換
  • 最後に16進数に変換

いくつかあるフォーマット(NEC / 家製協(AEHA) / SONY)の中から今回はNECフォーマットの解説を。

そもそも赤外線パターンの数値何?

赤外線LEDを8.925ミリ秒ONにして、4.540ミリ秒OFFにして、5.14ミリ秒ONにする...って意味です。

[8925, 4540, 514, 626, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 626, 514]

この先出る T(変調単位) について

NECフォーマットの場合はおおよそ562が使われています。リモコンによってばらつきがあると思う。
ので、Tの1倍って書いてあった場合は、T*1 = 562Tの3倍って書いてあった場合はT*3 = 1686ってことです

赤外線パターンの構造

+---------------------------------------------------------------------------------------------------
| リーダー部 2bit | データ部 32bit         | トレーラー部 1bit |
+---------------------------------------------------------------------------------------------------

上記のアスキーアートのようになってます。良くわからんと思いますのでもう少し解説続けます。

リーダー部

最初のON、OFFのデータがリーダー部となっております。
NECフォーマットの場合おおよそ、ONは9ミリ秒(Tの16倍)、OFFは4.5ミリ秒(Tの9倍)に近い値になるみたいです。

実際のデータ(省略版)を見てもNECフォーマットっぽいです。(NECのシーリングライトなのでそれはそう)

[
    8925, 4540, // ここが 9000 , 4500 ぐらいならNECフォーマット
    514, 626,
    514,
]

データ部

3番目からデータになっています。ここで見てほしいのは右と左のペア(下のコードでは隣同士に整形しておいた)です。
実際のデータを見るとなんか近い値明らかに2、3倍になってる数値が見えてくると思います。

[
    8925, 4540, // リーダー部 NECフォーマットと判明
    514, 626, // 近い数値だな...
    514, 1730,  // 3倍ぐらいになってる!?
    514, 626,  // 近い数値だな...
    514
]

NECフォーマットの仕様では、

ONが Tの1倍 -> Tの1倍 の順番になってて、データは0
OFFが Tの1倍 -> Tの3倍の順番になっていて、データは1になっているみたいです。?

実際のデータにコメントとして書き足すと

[
    8925, 4540, // リーダー部 
    514, 626, // ON データ0
    514, 1730,  // OFF データ1
    514, 626,  // ON データ0
    514
]

って感じになっています。514->626で正確に1倍にはなっていないのですが多分データ0で合ってます。
この01がNECフォーマットでは32個続きます(32bit

32bitのデータの中身

16bitで区切ります。
区切って、前の16bitがカスタマーコード、後ろの16bitがデータです。

0100000110110110 0110010110011010

後ろの16bitは、さらに8bitに分けることが出来ます。

01100101 10011010

前の8bitがデータ、後ろの8bitは前の8bitの反転になっていて、正しいデータか検証出来るようにしているそうです。(0なら1、1なら0)

01100101 // 前の8bit
10011010 // 後ろの8bit。前の8bitと比べて0と1が反転している。

トレーラー部

よくわからん。取得したデータの最後のTの1倍の値がそうだと思う。

データ部を2進数に変換する

取得したデータの中から、まずリーダー部(最初の2個)とトレーラー部(最後の1個)を消します。

解説用に短くしています。

[8925, 4540, 514, 626, 514, 1730, 514, 626, 514]

こんな感じ?

[514, 626, 514, 1730, 514, 626]

次に、[1,2][3,4]...のようにまとめていきます

[[514, 626], [514, 1730], [514, 626]]

その後、まとめた数値を比較して、最初の値を2倍してももう一方が大きい場合は1、そうでない場合は0を入れていきます
仕様ではTの3倍です。がばらつきがあるのでしゃーない。

[
    [514, 626],  // 2倍以上ではない => 0
    [514, 1730],  // 2倍以上 => 1
    [514, 626]  // 2倍以上ではない => 0
]

こんな風に

[0, 1, 0]

そしたらくっつけて完成です。お好みで16進数に変換してもいいと思う。

let bin = "010";

16進数(2進数)を赤外線パターンに変換する

この場合は、16進数(もしくは2進数)の赤外線のデータがどこのフォーマットのものなのかがわかっている必要があります。
16進数のデータにはリーダー部が含まれていないため分かりません。

今回はNECフォーマットだとして、T562という設定で行きます。

まず、配列を用意して、リーダー部を追加しておきます。

[8992, 5058]

そしたら、16進数の場合は2進数に戻します。
戻したら、データ1が[Tの1倍,Tの3倍]、0が[Tの1倍,Tの1倍]であることがNECフォーマットの仕様に書いてあるので、そのとおり作ります。

let bin = "010";
[
    8992, 5058, 
    562, 562, // 0
    562, 1686, // 1
    562, 562 // 0
]

最後にストップビットとして配列の最後にTの1倍を入れれば終わり。

[8992, 5058, 562, 562, 562, 1686, 562, 562, 562]

以下変換用のコード。Kotlin版

fun main() {
    val pattern = listOf(
        8925, 4540, 514, 626, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 1730, 514, 626, 514, 1730, 514, 1730, 514, 626, 514, 626, 514, 626, 514, 626, 514, 626, 514, 1730, 514, 626, 514
    )
    // グループ化
    val onOffGroupList = patternToOnOffPairList(pattern)
    println("赤外線パターン ---")
    println(onOffGroupList)
    // データ部2進数変換
    val bin = patternToBinCode(pattern)
    println("データ部 2進数 ---")
    println(bin)
    // データ部16進数変換
    val hex = binStringToHexString(bin)
    println("データ部 16進数 ---")
    println(hex)
}

/**
 * [8955, 4510]を[Pair(8955, 4510)]にしていく関数。戻す際はflatMapを使ってください。
 * */
fun patternToOnOffPairList(patternList: List<Int>) =
    patternList
        .toMutableList()
        .mapIndexed { index, _ ->
            if ((index + 1) % 2 != 0) {
                Pair(patternList[index], patternList.getOrNull(index + 1) ?: 0)
            } else null
        }
        .filterNotNull()

/**
 * [patternList]のデータ部を2進数にして返す。
 *
 * ONとOFFが1:3の比率(だいたい)の場合は1、違う場合は0になる
 *
 * 32bit(32文字)になるはず
 *
 * リーダー部(9000,4500)(だいたいONが9000、OFFが4500)の次からがデータ部なので
 *
 * 例
 *
 * (607, 520) =(ON607、OFF520) なら 0
 * (607, 1703) =(ON607、OFF1703)なら 1
 *
 * 変換例
 * 010000 ...
 *
 * @param patternList ON/OFFパターン配列
 * */
fun patternToBinCode(patternList: List<Int>) =
    patternToOnOffPairList(patternList)
        .drop(1) // リーダー部を消す
        .dropLast(1) // ストップビット部も消す
        .map { (on, off) -> if (off > on * 2) "1" else "0" } // ONの2倍以上で T*3 ってことで
        .joinToString(separator = "") { it }

/**
 * 2進数からパターン生成。先頭にトレーラーつけて、最後にストップビットを入れる
 *
 * @param t 変調。NECなら 562 前後?
 * @param binCode 2進数
 * */
fun binCodeToPattern(t: Int, binCode: String) =
    listOf(t * 16, t * 9) + binCode.toList().flatMap { if (it == '1') listOf(t * 1, t * 3) else listOf(t * 1, t * 1) } + listOf(t * 1) // 1なら[T*1,T*3]、0なら[T*1,T*1]を配列に足していく

/**
 * 2進数の文字列を16進数の文字列に変換する
 *
 * "1010"を"A"に変換する
 *
 * @param binString 変換前2進数の文字列
 * */
fun binStringToHexString(binString: String) = "0x" + binString.toInt(2).toString(16)

出力例です。

赤外線パターン ---
[(8925, 4540), (514, 626), (514, 1730), (514, 626), (514, 626), (514, 626), (514, 626), (514, 626), (514, 1730), (514, 1730), (514, 626), (514, 1730), (514, 1730), (514, 626), (514, 1730), (514, 1730), (514, 626), (514, 626), (514, 1730), (514, 1730), (514, 1730), (514, 1730), (514, 1730), (514, 626), (514, 1730), (514, 1730), (514, 626), (514, 626), (514, 626), (514, 626), (514, 626), (514, 1730), (514, 626), (514, 0)]
データ部 2進数 ---
01000001101101100111110110000010
データ部 16進数 ---
0x41b67d82

長くなるのでGitHubに置いておきますが、データ部とかも見れる完全版も置いておきます。

https://github.com/takusan23/IrPatternToBin