3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

M5StickC plus のMicropythonで赤外線リモコン

Last updated at Posted at 2021-03-14

M5シリーズの赤外線リモコンの実装はArduino環境ではたくさんあったが、Micropythonでのやり方は
あまり書いてなかったので自分用も兼ねてメモ

環境

  • M5StickC plus
  • M5 UI Flow v1.7.2

結論

esp32.RMT モジュールを使う
https://docs.micropython.org/en/latest/library/esp32.html?highlight=rmt#esp32.RMT

esp32.RMT

公式ドキュメントに記載されている通り、赤外線リモコン用に用意されているモジュール。
ソースの周波数が80MHz固定、送信用機能しかないなど、まだベータ版としての位置づけみたい。

clock_divRMT.write_pulseでの時間分解能を指定。
分解能は1 / (source_freq / clock_div)で計算できる。

RMT.write_pulses(pulses, start)pulsesにON/OFFの時間の
リストまたはタプルを渡してあげることで、指定したキャリアのパルスが発生する。
startpulseの最初がON/OFFのどちらか。

# To use carrier frequency
r = esp32.RMT(channel=0, pin=Pin(18), clock_div=80, carrier_freq=38000, carrier_duty_percent=33)
r  # RMT(channel=0, pin=18, source_freq=80000000, clock_div=80, carrier_freq=38000, carrier_duty_percent=33)

# The channel resolution is 1us (1/(source_freq/clock_div)).
r.write_pulses((1, 20, 2, 40), start=0)  # Send 0 for 1us, 1 for 20us, 0 for 2us, 1 for 40us

実装と確認

我が家の エアコン(Panasonic) で試してみた。
Panasonicは家製協のフォーマットであるので、下記を参考に実装してM5 UI Flowから書き込み1して動作したことを確認。
https://github.com/r45635/HVAC-IR-Control/blob/master/Protocol/Panasonic%20HVAC%20IR%20Protocol%20specification.pdf

リモコンがどんなコードを送信しているかなどの解析は割愛。
https://github.com/gensyu/micropython_ir_send

# MicroPython esp32 AEHA 赤外線リモコン送信
import time

T = 436
HEADER_HIGH_US = 3400
HEADER_LOW_US = 1750


def reverce_8bit(data_8bit: int) -> int:
    """8bit を反転させる

    Parameters
    ----------
    data_8bit : int
        8bit数値

    Returns
    -------
    int
        ビット反転後の数値
    """

    data_8bit = ((data_8bit & 0b01010101) << 1) | ((data_8bit & 0b10101010) >> 1)
    data_8bit = ((data_8bit & 0b00110011) << 2) | ((data_8bit & 0b11001100) >> 2)
    return (data_8bit & 0b00001111) << 4 | data_8bit >> 4


def cal_parity(customer_code: list) -> int:
    """パリティ計算

    Parameters
    ----------
    customer_code : int
        カスタマーコード

    Returns
    -------
    int
        パリティ計算結果
    """
    parity = 0b0000
    for i in customer_code:
        upper_4bit = i >> 4
        lower_4bit = i & 0b00001111
        parity = parity ^ upper_4bit ^ lower_4bit
    return parity


def cal_sum(data_lsb: list) -> int:
    """チェックサム計算

    Parameters
    ----------
    data_lsb : list
        データ配列

    Returns
    -------
    int
        チェックサム値
    """
    val = 0
    for i in data_lsb:
        val = (val + i) % 256
    return val


def encode_ir_data(customer_code_lsb: list, data_lsb: list) -> str:
    """カスタマーコード、データを0, 1文字列に変換

    Parameters
    ----------
    customer_code : List[int]
        カスタマーコード
    data : List[int]
        送信データ

    Returns
    -------
    str
        0,1文字列
    """
    bit_str = ""
    customer_code_msb = [reverce_8bit(i) for i in customer_code_lsb]
    for d in customer_code_msb:
        bit_str += "{0:08b}".format(d)
    bit_str = bit_str + "{:04b}".format(cal_parity(customer_code_msb))

    data_msb = [reverce_8bit(i) for i in data_lsb]
    check_sum = cal_sum(customer_code_lsb + data_lsb)
    data_msb.append(reverce_8bit(check_sum))
    for i, d in enumerate(data_msb):
        if i == 0:
            bit_str += "{:04b}".format(d)
        else:
            bit_str += "{:08b}".format(d)
    return bit_str


def generate_frame(bit_code: str) -> list:
    """01文字列からON/OFFの時間に変換

    Parameters
    ----------
    bit_code : str
        0,1文字列

    Returns
    -------
    list
        ON/OFF時間の配列
    """
    pulse_width_data = [HEADER_HIGH_US, HEADER_LOW_US]
    for bit in bit_code:
        if bit == "0":
            pulse_width_data.extend([T, T])
        else:
            pulse_width_data.extend([T, 3 * T])
    pulse_width_data.extend([T])
    return pulse_width_data


def send_ir_data(rmt, customer_code, *data):
    if len(data) == 0:
        raise TypeError("data引数がありません")
    for d in data:
        bit_code = encode_ir_data(customer_code, d)
        frame = generate_frame(bit_code)
        rmt.write_pulses(frame, start=1)
        while not (rmt.wait_done()):
            time.sleep_us(T)
        time.sleep_us(8000)

if __name__ == "__main__":
    CUSTOMER_CODE = [0x02, 0x20]
    DATA1 = [0xE0, 0x04, 0x00, 0x00, 0x00]
    DATA2 = [
        0xE0,
        0x04,
        0x00,
        0x41,
        0x30,
        0x80,
        0xAF,
        0x00,
        0x00,
        0x06,
        0x60,
        0x00,
        0x00,
        0x80,
        0x00,
        0x06,
    ]

    ir_led = RMT(0, pin=Pin(9), clock_div=80, carrier_freq=38000)
    send_ir_data(ir_led, CUSTOMER_CODE, DATA1, DATA2)


  1. RMTチャンネル割当て後、deinit()せずに同じチャンネルに割当するとエラー測れるので、書込み毎にリセットする必要あり

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?