/      English

プログラムからインタラクティブモードを使う

プログラムから自動的にインタラクティブモードにアクセスする方法

インタラクティブモードはシリアルポートを利用するため、機械的に操作することもできます。

生産工程においては、インタラクティブモードを自動的に利用したい場面があるかもしれません。Python を使って設定値を読み出す例を紹介します。

インタラクティブモードの仕組み

初期状態では、TWELITE の RX ポートは 115200bps 8N1 の設定で動作しています。インタラクティブモードに遷移するには、200msから1000ms程度の間隔で + を3回入力します。インタラクティブモードの操作はASCII文字を入力するだけの処理であることから、インタラクティブモードに入ってしまえば、あとは自由にプログラム操作することができます。

実装例

Pythonによる設定値の読み出し

ここでは、シリアル通信アプリApp_Uartを書き込んだTWELITEから、TWELITE R2/R3 を通じてシリアルIDと設定値を読み出すスクリプトを記載します。

スクリプト

Python 3.12 を使って実装しています。

get_serial_id_and_settings()関数を再利用できます。

# -*- coding: utf-8 -*-

import time
import re

from tweliter import Tweliter  # Mono Wireless module for TWELITE R devices


def get_serial_id_and_settings() -> tuple[str, dict[str, str]] | None:
    """
    Get the serial ID and settings from the interactive mode of a TWELITE R device.

    Returns:
        tuple[str, dict[str, str]] | None:
            A tuple containing:
            - str: the serial ID (e.g., "0x82010079")
            - dict[str, str]: the settings dictionary (e.g., {'a': '0x67720103', ...})
            Returns None if the device cannot be accessed or the output is invalid.

    Notes:
        Example output from the interactive mode of an App_Uart device:
        --- CONFIG/TWE UART APP V1-04-6/SID=0x82010079/LID=0x78 -- ---
         a: set Application ID (0x67720103)
         i: set Device ID (120=0x78)
         c: set Channels (18)
         x: set RF Conf (3)
         r: set Role (0x0)
         l: set Layer (0x1)
         b: set UART baud (38400)
         B: set UART option (8N1)
         m: set UART mode (E)
         k: set Tx Trigger (sep=0x0d0a, min_bytes=0 dly=0[ms])
         h: set header format [;U;%t;%i;0x%A;%q;%s;<*;%X;\n]
         C: set crypt mode (0)
         o: set option bits (0x00000100)
        ---
         S: save Configuration
         R: reset to Defaults
    """
    try:
        with Tweliter(
            type_filter=Tweliter.Type.TWELITE_R2 | Tweliter.Type.TWELITE_R3
        ) as liter:
            # Reset the device
            liter.reset_device()

            # Get the serial port instance (pyserial)
            ser = liter.get_serial_instance()
            ser.timeout = 1.0

            # Enter to the interactive mode
            for _ in range(3):
                ser.write("+")
                time.sleep(0.3)

            # Read the output
            raw_output = ser.read_until(b"S:").decode("utf-8")

            # Reset the device (Exit interactive mode)
            liter.reset_device()
    except IOError as e:
        print(f"Couldn't open the device {e}")
        return None

    # Find the settings block in the output
    filter_output = re.search(r"---(.*?)---(.*?)---", raw_output, re.DOTALL)
    if filter_output:
        information_line = filter_output.group(1)
        settings_block = filter_output.group(2)
    else:
        print("No settings block found.")
        return None

    # Extract serial id from the information line
    serial_id_match = re.search(r"SID=0x([0-9A-Fa-f]+)", information_line)
    if serial_id_match:
        serial_id = f"0x{serial_id_match.group(1)}"
    else:
        print("Serial ID not found.")
        return None

    # Extract key-value pairs (str,str) from the settings block
    settings_dict = dict(
        re.findall(r"^\s*(\w):.*?\(([^()]*)\)", settings_block, re.MULTILINE)
    )

    return serial_id, settings_dict


def main() -> None:
    # Get the serial ID and settings from the device
    result = get_serial_id_and_settings()
    if result is None:
        print("Failed to retrieve serial ID and settings.")
        return
    serial_id, settings = result

    # Show the results
    print(f"Serial ID: {serial_id}")
    for id, value in settings.items():
        match id:
            case "a":
                print(f"Application ID: {value}")
            # case "i":
            #     print(f"Logical ID: {value}")
            # case "c":
            #     print(f"Channel: {value}")


if __name__ == "__main__":
    main()

解説

インタラクティブモードの出力を読み出す流れ
  1. TWELITE をリセットする
    • リセットにより、インタラクティブモードに入るための初期状態に戻します。ここでのリセットは、TWELITE R2/R3 のリセットピン制御によって行います。
  2. シリアルポートのインスタンスを取得する
    • pyserial により、PC 側から TWELITE のシリアル入出力にアクセスするためのインスタンスを取得します。このインスタンスを通じて、後続のコマンド送信やデータの読み出しを実行できます。
  3. +を3回入力する
    • 一定間隔(200〜1000ms)で + を3回送信することで、TWELITE はインタラクティブモードに遷移します。この方法はユーザー手動での操作と同様です。
  4. 保存コマンドの説明 S: まで読み取る
    • インタラクティブモードでは、設定値が並んだあとに S: という保存コマンドの説明行が現れるため、そこまで読み取ることで一連の設定内容をすべて取得できます。
  5. TWELITE をリセットする
    • 読み出しが終わったら、再度リセットしてインタラクティブモードから通常モードに戻します。
with Tweliter(
    type_filter=Tweliter.Type.TWELITE_R2 | Tweliter.Type.TWELITE_R3
) as liter:
    # Reset the device
    liter.reset_device()

    # Get the serial port instance (pyserial)
    ser = liter.get_serial_instance()
    ser.timeout = 1.0

    # Enter to the interactive mode
    for _ in range(3):
        ser.write("+")
        time.sleep(0.3)

    # Read the output
    raw_output = ser.read_until(b"S:").decode("utf-8")

    # Reset the device (Exit interactive mode)
    liter.reset_device()
インタラクティブモードの出力を解釈する流れ
  1. 一行目と --- に挟まれたブロックを抽出する

    • re.search(r"---(.*?)---(.*?)---", raw_output, re.DOTALL) により、インタラクティブモードの出力から設定全体の情報を2つの部分に分けて取り出します。最初のキャプチャグループは、シリアルIDなどが含まれる情報行(information_line)、2つ目は設定項目が並ぶ設定値ブロック(settings_block)です。re.DOTALL により、改行を含む全文検索が可能になります。
  2. 一行目からシリアルIDを抽出する

    • re.search(r"SID=0x([0-9A-Fa-f]+)", information_line) を使って、SID=0x に続く16進数のID値(例:82010079)を取得し、それを 0x82010079 のような文字列に整形します。16進数は大文字小文字を区別せずに抽出されます。
  3. ブロックからコマンドIDと値のペアを抽出する

    • re.findall(r"^\s*(\w):.*?\(([^()]*)\)", settings_block, re.MULTILINE) を使って、設定値の各行から a: ... (値) のような形式を対象に、コマンドID(1文字)と括弧内の値を取り出します。先頭の空白、コロン以降の任意文字列をスキップし、最初に見つかる括弧内の値のみを抽出します。re.MULTILINE により、複数行に対応した検索ができます。
# Find the settings block in the output
filter_output = re.search(r"---(.*?)---(.*?)---", raw_output, re.DOTALL)
if filter_output:
    information_line = filter_output.group(1)
    settings_block = filter_output.group(2)
else:
    print("No settings block found.")
    return None

# Extract serial id from the information line
serial_id_match = re.search(r"SID=0x([0-9A-Fa-f]+)", information_line)
if serial_id_match:
    serial_id = f"0x{serial_id_match.group(1)}"
else:
    print("Serial ID not found.")
    return None

# Extract key-value pairs (str,str) from the settings block
settings_dict = dict(
    re.findall(r"^\s*(\w):.*?\(([^()]*)\)", settings_block, re.MULTILINE)
)

return serial_id, settings_dict