async def rx_loop(bus: RS485Stream):
    # コマンド待ちループ
    while True:
        fr = await bus.recv_frame(max_len=64)
        addr, cmd, payload = fr
        # 宛先=自分宛のみ受理（CMD_TOKENはassignedのみが受ける設計）
        if addr != MY_ADDR:
            continue

        # ジョブ割り当ての受信
        if cmd == CMD_ASSIGN and len(payload) == 1 + 12:
            seq = payload[0]
            imgW, imgH, x0, y0, w, h = struct.unpack("<HHHHHH", payload[1:1+12])
            # 古いファイルを消す
            if tile_path: os.remove(tile_path)
            # 制御ノードにACKを返す
            await bus.send_frame(addr=addr, cmd=CMD_ACK, payload=bytes([seq]))

        # データ送信要求受信
        elif cmd == CMD_TOKEN:
            ids = list(payload)
            # 自分の番：タイル送信
            header = struct.pack("<HHHH", x0, y0, w, h)
            total_len = 8 + 2*w*h
            # データ送信
            bus.send_frame_streaming_sync(MASTER_ADDR, CMD_TILE, total_len,
                                          header=header,
                                          chunk_iter=iter_lines_from_flash(),
                                          interline_gap_ms=2)
            # 次ノードへトークン(データ送信が必要なノード一覧)を渡す
            rest = ids[1:]
            if rest:
                await bus.send_frame(rest[0], CMD_TOKEN, bytes(rest))