技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

家製協(AEHA)フォーマットを 16 進数で読み取ってみた

white-azalea.hatenablog.jp

ここで読み取ったエアコン起動コードをそのまま送りつけてもうまくエアコンがつかなかったので、フォーマットを解読できるのかやってみた。
お題は送りつけに失敗した下記

[3531, 1700, 448, 415, 446, 1289, 453, 409, 452, 411, 451, 410, 451, 412, 448, 414, 448, 414, 446, 416, 445, 417, 444, 418, 453, 409, 452, 411, 450, 1285, 447, 415, 446, 416, 446, 417, 453, 408, 453, 409, 452, 411, 450, 412, 449, 1286, 447, 1289, 453, 1283, 449, 413, 448, 414, 448, 1288, 455, 407, 453, 409, 452, 411, 450, 412, 448, 414, 447, 415, 446, 416, 445, 417, 444, 418, 453, 410, 451, 410, 452, 410, 451, 411, 450, 413, 447, 415, 447, 415, 445, 418, 443, 418, 453, 409, 452, 410, 451, 411, 450, 412, 449, 414, 451, 411, 446, 416, 445, 417, 444, 423, 448, 409, 452, 410, 453, 409, 450, 1286, 448, 414, 447, 1289, 453, 409, 452, 410, 454, 408, 449, 414, 448, 413, 447, 420, 441, 417, 444, 1292, 450, 1285, 448, 1287, 445, 418, 443, 419, 452, 1283, 449, 414, 447, 415, 447, 415, 445, 417, 445, 417, 454, 408, 452, 412, 449, 412, 449, 413, 448, 415, 446, 415, 446, 416, 445, 417, 444, 418, 454, 409, 451, 411, 450, 413, 448, 1286, 446, 1289, 453, 410, 452, 411, 449, 412, 449, 1286, 447, 417, 443, 418, 453, 1282, 451, 1286, 446, 416, 445, 417, 444, 418, 454, 409, 452, 410, 450, 412, 449, 413, 448, 414, 447, 415, 446, 1290, 453, 1282, 450, 1286, 446, 1290, 453, 1282, 450, 413, 448, 1288, 445, 417, 443, 1292, 451, 412, 449, 413, 448, 414, 447, 415, 446, 417, 444, 418, 453, 410, 451, 410, 451, 411, 450, 413, 448, 414, 447, 415, 446, 417, 444, 417, 444, 418, 453, 409, 452, 410, 451, 411, 450, 412, 449, 413, 448, 414, 447, 415, 446, 417, 444, 418, 443, 419, 453, 1283, 449, 1286, 446, 416, 445, 417, 444, 418, 453, 410, 451, 411, 450, 412, 449, 413, 448, 414, 446, 416, 445, 417, 444, 418, 443, 419, 452, 410, 451, 411, 450, 412, 449, 414, 447, 415, 446, 416, 449, 413, 444, 419, 453, 409, 452, 414, 447, 411, 450, 413, 448, 1287, 444, 418, 443, 420, 453, 408, 451, 411, 451, 411, 450, 413, 448, 414, 446, 416, 445, 417, 444, 1292, 451, 1284, 447, 416, 450, 1285, 453, 409, 452, 410, 451, 411, 450, 1286, 446, 1289, 454, 410, 451, 411, 450, 1285, 447, 415, 447, 416, 444, 1291, 452]

8T4T で始まってるはずなので調べてみると

>>> 3531 / 8
441.375
>>> 1700 / 4
425.0

あれ?
実際には T は T = 350~500μs とか抜かしてるので、ヘッダが 1T という仕様に合わせて、その値を使ってみるか

data = input_code[2:]
bit_header = data[0::2]
T = sum(bit_header) / len(bit_header)
print('T = ' + str(T))

としたら T = 448.6102564102564 みたいなので、 449 で割ってみると

>>> 3531 / 449
7.864142538975501
>>> 1700 / 449
3.7861915367483294

四捨五入すればこんなもんかと思わなくもない数字。
この論理で読んでいくか

ということで読んでみた

読み取りしてみたコードは次の通り

input_code = [3531, 1700, 448, 415, 446, 1289, 453, 409, 452, 411, 451, 410, 451, 412, 448, 414, 448, 414, 446, 416, 445, 417, 444, 418, 453, 409, 452, 411, 450, 1285, 447, 415, 446, 416, 446, 417, 453, 408, 453, 409, 452, 411, 450, 412, 449, 1286, 447, 1289, 453, 1283, 449, 413, 448, 414, 448, 1288, 455, 407, 453, 409, 452, 411, 450, 412, 448, 414, 447, 415, 446, 416, 445, 417, 444, 418, 453, 410, 451, 410, 452, 410, 451, 411, 450, 413, 447, 415, 447, 415, 445, 418, 443, 418, 453, 409, 452, 410, 451, 411, 450, 412, 449, 414, 451, 411, 446, 416, 445, 417, 444, 423, 448, 409, 452, 410, 453, 409, 450, 1286, 448, 414, 447, 1289, 453, 409, 452, 410, 454, 408, 449, 414, 448, 413, 447, 420, 441, 417, 444, 1292, 450, 1285, 448, 1287, 445, 418, 443, 419, 452, 1283, 449, 414, 447, 415, 447, 415, 445, 417, 445, 417, 454, 408, 452, 412, 449, 412, 449, 413, 448, 415, 446, 415, 446, 416, 445, 417, 444, 418, 454, 409, 451, 411, 450, 413, 448, 1286, 446, 1289, 453, 410, 452, 411, 449, 412, 449, 1286, 447, 417, 443, 418, 453, 1282, 451, 1286, 446, 416, 445, 417, 444, 418, 454, 409, 452, 410, 450, 412, 449, 413, 448, 414, 447, 415, 446, 1290, 453, 1282, 450, 1286, 446, 1290, 453, 1282, 450, 413, 448, 1288, 445, 417, 443, 1292, 451, 412, 449, 413, 448, 414, 447, 415, 446, 417, 444, 418, 453, 410, 451, 410, 451, 411, 450, 413, 448, 414, 447, 415, 446, 417, 444, 417, 444, 418, 453, 409, 452, 410, 451, 411, 450, 412, 449, 413, 448, 414, 447, 415, 446, 417, 444, 418, 443, 419, 453, 1283, 449, 1286, 446, 416, 445, 417, 444, 418, 453, 410, 451, 411, 450, 412, 449, 413, 448, 414, 446, 416, 445, 417, 444, 418, 443, 419, 452, 410, 451, 411, 450, 412, 449, 414, 447, 415, 446, 416, 449, 413, 444, 419, 453, 409, 452, 414, 447, 411, 450, 413, 448, 1287, 444, 418, 443, 420, 453, 408, 451, 411, 451, 411, 450, 413, 448, 414, 446, 416, 445, 417, 444, 1292, 451, 1284, 447, 416, 450, 1285, 453, 409, 452, 410, 451, 411, 450, 1286, 446, 1289, 454, 410, 451, 411, 450, 1285, 447, 415, 447, 416, 444, 1291, 452]

# ※ リピートデータ(8T, 8T データ)は考慮してないのであしからず

# 家製協(AEHA)フォーマット
# 参考の単位フレーム
T = 445

# ヘッダ部分
header = input_code[:2]
# print([int(n / 440) for n in header])

# データ部分
data = input_code[2:]

# bit header and bit body
bit_header = data[0::2]
bit_body = data[1::2]

# ヘッダは 1T と決まってるので、平均値から T の正式な数字を探してみる
T = int(sum(bit_header) / len(bit_header))
print('T = ' + str(T))

def extract_bit(num: int) -> str:
    '''
    与えられたデータを家製協(AEHA)フォーマット的なビット判定をして文字で返す
    1=1T1T, 0=1T3T で、偶数番目ビットだけ長さを見ればよく、2T 以上 = 0 とみなせば良さそう?
    '''
    return '1' if num > (T * 2) else '0'


def extract_to_bits(half_bite_arr: list) -> str:
    bit_str = ''.join([extract_bit(n) for n in half_bite_arr])
    #print(bit_str)
    return bit_str


def extract_halfbyte(bitList: list) -> list:
    '''
    与えられた赤外線ビット列を 4 ビット単位(16進数1文字)で整数リスト化する。
    余りがあると警告文字列を表示
    '''
    num_of_byte = int(len(bitList) / 4)
    mod_bits = len(bitList) % 4
    result = []
    for n in range(0, num_of_byte):
        half_bite_arr = bitList[n*4: n*4+4]
        half_byte_bits = extract_to_bits(half_bite_arr)
        #print(half_byte_bits)
        result.append(int(half_byte_bits, 2))
    if mod_bits > 0:
        mod_bit_arr = bitList[-mod_bits:]
        bit_bit_binary = ''.join([extract_bit(n) for n in mod_bit_arr])
        print('Exists cant extract bits: ' + str(mod_bit_arr) + '(' + bit_bit_binary + ')')
    return result


def as_hex_str(hex_arr):
    '''
    数字配列を 16 進数で応答する
    '''
    hex_str = ''
    for i in range(0, len(hex_arr)):
        hex_str += hex(hex_arr[i])[2:]
        if i % 2 == 1:
            hex_str += ' '
    return hex_str


# 16bitのカスタマーコード
customer_bits = bit_body[:16]
# print(len(customer_bits))
# 16 進数カスタマーコード
hex_customer = extract_halfbyte(customer_bits)
print('customer code: ' + as_hex_str(hex_customer))

# 4 bit のパリティ
parity_bits = bit_body[16:20]
parity_bit_str = extract_to_bits(parity_bits)
print('parity bits  : ' + parity_bit_str)

# その後のデータ
other_data = bit_body[20:]
hex_others = extract_halfbyte(other_data)
print('other code   : ' + as_hex_str(hex_others))

その結果がコレ

T = 448
customer code: 40 04 
parity bits  : 0000
Exists cant extract bits: [416, 1291](01)
other code   : 72 00 00 00 05 01 c8 00 03 13 00 7d 40 00 00 18 00 00 04 01 a3 2

25 度という情報はどこいったのか…

単純にビット配列文字列にするとこんな感じ
011100100000000000000000000000000000010100000001110010000000000000000011000100110000000001111101010000000000000000000000000110000000000000000000000001000000000110100011001001

仕様書見ないとデータ部の構成はわからんね…

改めて逆変換

上記コードでバイナリ文字列抽出してしまえば結構簡単に欲しい情報は取れる

binary_str = '011100100000000000000000000000000000010100000001110010000000000000000011000100110000000001111101010000000000000000000000000110000000000000000000000001000000000110100011001001'

T = 448

def enc_bit2sig(bit: str) -> list:
    return [T, T] if bit == '0' else [T, 3*T]

# ヘッダシグナル
header_sig = [8*T, 4*T]
vender_sig = []

all_sig = header_sig[:]

# ベンダーコード
for b in '0100000000000100':
    all_sig += enc_bit2sig(b)

# ベンダーパリティ
for b in '0000':
    all_sig += enc_bit2sig(b)

# データ部
for b in binary_str:
    all_sig += enc_bit2sig(b)

print(all_sig)

とすると

[3584, 1792, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 1344, 448, 1344, 448, 1344, 448, 448, 448, 1344, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 448, 448, 1344, 448, 1344, 448, 448, 448, 448, 448, 1344, 448, 448, 448, 448, 448, 1344]

コレを書き込んで見たが…
認識されずか…なんかノイズでも拾ってるのかなーと、cirguitplayground を読み取りモードでリモコンも受けずに放置してると…

Heard 7 Pulses: [181, 1679, 178, 5799, 188, 2765, 181]
----------------------------
Heard 1 Pulses: [219]
----------------------------
Heard 1 Pulses: [180]
----------------------------
Heard 1 Pulses: [171]
----------------------------
Heard 1 Pulses: [171]
----------------------------
Heard 9 Pulses: [276, 249, 227, 812, 180, 1011, 704, 5838, 2286]
----------------------------
Heard 5 Pulses: [625, 550, 2179, 5754, 2192]
----------------------------
Heard 7 Pulses: [624, 459, 681, 387, 1191, 5821, 1815]
----------------------------
Heard 3 Pulses: [181, 5201, 4674]
----------------------------
Heard 5 Pulses: [578, 265, 2587, 106, 1378]
----------------------------
Heard 1 Pulses: [4267]
----------------------------

誰だおまえええええ(TT

結局短いコードでもないとノイズ拾って正しい設定を拾えないご様子…。
物理的な問題となると物理で解決…となるのだろうけど、真面目にリモコン操作しようと思ったら rasberrypi にリモコンモジュール買ってきて組み込んだ方が良さそうだ…