/

UART機能によるPCとの連携

親機をPCへ接続して、UARTを通じたデータの受信と送信を行う
UART 通信を利用することで、親機を PC と連携させることができます。

使用する製品

TWELITE DIPTWELITE R2
TWELITE 親機/子機USB アダプター
超簡単!標準アプリ-
2個1個

なお、TWELITE DIP と TWELITE R2 のペアは MONOSTICK 単体と同等です。次の組み合わせでも構いません。

TWELITE DIPMONOSTICK
TWELITE 子機TWELITE 親機
超簡単!標準アプリ親機・中継機アプリ
1個1個

TWELITE STAGE アプリの導入

TWELITE STAGE アプリは、ファームウェアの設定や書き換えに加え、親機との送受信を評価するための機能を備えたツールです。

TWELITE STAGE アプリは、TWELITE STAGE SDK に含まれています。

TWELITE STAGE アプリによる受信

超簡単!標準アプリの子機が送信するデータは、DIx/AIxポートの入力状態や電源電圧、送信元の論理デバイスIDといった情報を含んでいます。

シリアル文字列の表示

親機が子機から受信したデータは、親機がシリアル通信(UART)で出力する文字列を解釈することで取得できます。まずはこの文字列を表示してみましょう。

TWELITE STAGE アプリを立ち上げ、シリアルポート選択で親機を選択します。メインメニュー「1: ビューア」「1: ターミナル」を開いてください。

子機のデータを受信すると、次のようなメッセージを表示します。

:78811501C98201015A000391000C2E00810301FFFFFFFFFB

親機が出力するこのような文字列を解釈することで、子機が送信したデータを得ることができます。

なお、TWELITE の親機が出力する文字列は、基本的に次の形式へ従います。

ヘッダペイロードチェックサムフッタ
:00-FFの繰り返しペイロードのLRC8CRLF
  • すべて ASCII 文字※
  • 先頭は : (0x3A)
  • 末端は CRLF (\r\n/0x0D 0x0A)
  • ビッグエンディアン

※ シリアル通信アプリのバイナリ書式を除く

標準アプリ ビューア

上記のような形式はコンピュータにやさしいものですが、人間が確認するにはこれを解釈する必要があります。TWELITE STAGE アプリには、超簡単!標準アプリの子機が送信したデータを表す文字列を解釈して表示する機能があります。後述の Python ライブラリはこの解釈を行います。

メインメニュー「1: ビューア」へ戻り、「2: 標準アプリ ビューア」を開いてください。

タイムスタンプと論理デバイスID、シリアルIDとDIx/AIxの値を確認できます。

標準アプリ ビューアの画面

標準アプリ ビューアの画面

TWELITE STAGE アプリによる送信

これまでとは反対に、親機から子機へ無線パケットを送信し、子機の出力状態を変更することもできます。

コマンダー

メインメニュー「1: ビューア」へ戻り、「5: コマンダー」を開いてください。

DIxおよびAIxの入力をシミュレートしたパケットを送信できます。

コマンダーの画面

コマンダーの画面

使い方

  • 送信先と入力状態を選択
  • 送信をクリック

Python スクリプト

TWELITE STAGE アプリの「標準アプリ ビューア」および「コマンダー」は、どちらもシリアル通信によって文字列をやりとりしているに過ぎません。シリアル通信を利用できる環境であれば、自作のアプリケーションと親機を連携させることができます。

Python スクリプトを応用すれば、受信したデータを加工して保存したり、自作のソフトウェアから出力ポートを操作したりといった仕組みを実現できます。

MWings ライブラリの導入

MWings ライブラリは、TWELITE と Python スクリプトの連携を簡単に実現するためのライブラリです。具体的には、子機から受信したデータを表す文字列の解釈と、子機へ送信するデータの構築を行います。

PyPI からインストールできます。

Python スクリプトによる受信

TWELITE DIP のDI1へ接続したボタンが押された際にメッセージを表示する Python スクリプトを実装してみます。

サンプルスクリプトの実行

下記の内容のスクリプト dip_show_di1.py を作成してください。

# -*- coding:utf-8 -*-
# TWELTIE DIP start guide: receiving in python

import mwings as mw


def main():
    twelite = mw.Twelite(mw.utils.ask_user_for_port())

    @twelite.on(mw.common.PacketType.APP_TWELITE)
    def on_app_twelite(packet):
        if packet.di_state[0]:
            print("DI1 Pressed")

    try:
        twelite.daemon = True
        twelite.start()
        print("Started receiving")
        while True:
            twelite.join(0.5)
    except KeyboardInterrupt:
        print("Flushing...")
        twelite.stop()
        print("Completed")


if __name__ == "__main__":
    main()

スクリプトを実行すると、子機のDI1が Low になったときに DI1 Pressed と出力します。

実行時に複数の TWELITE R シリーズや MONOSTICK シリーズが接続されているときは、親機へ接続されたシリアルポートを選択してください。


poetry run python dip_show_di1.py
Multiple ports detected.
[1] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
[2] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
Select [1-2]: 2
Selected: /dev/cu.usbserial-R2xxxxxx
Started receiving
DI1 Pressed
DI1 Pressed
^CFlushing...
Completed

上記は macOS における例です。WindowsではCOMポート名を表示します。

サンプルスクリプトの解説

MWings ライブラリのインポート

import mwings as mw

短縮名 mw を使って呼び出せるようにしています。pandas as pdnumpy as np と同様です。

オブジェクトの作成

    twelite = mw.Twelite(mw.utils.ask_user_for_port())

親機をとやりとりするためのインタフェースとなる Twelite オブジェクトを作成しています。

引数にはシリアルポートを指定しますが、ここでは mw.utils.ask_users_for_port() ユーティリティを呼び出し、動的に決定しています。この関数はシリアルポートが存在しないときにエラーメッセージを出力し、1つだけ存在する場合はそのポートを、複数存在する場合はユーザが指定したポートを返します。

受信イベントハンドラの登録

    @twelite.on(mw.common.PacketType.APP_TWELITE)
    def on_app_twelite(packet):
        if packet.di_state[0]:
            print("DI1 Pressed")

超簡単!標準アプリの子機からのパケットを受信したときに呼び出されるイベントハンドラを登録しています。

イベントハンドラの登録は、Python のデコレータ(?)を使って行います。今回は超簡単!標準アプリ(App_Twelite)のデータを対象とするため、@twelite.on(mw.common.PacketType.APP_TWELITE) を記述しています。この記述の直後に定義された関数がハンドラとなります。関数名は問いませんが、Tweliteオブジェクトの初期化後に定義する必要があります。

今回は子機のDI1がLowになったことを検知するため、受信ハンドラが受け取る変数 packetmwings.parsers.app_twelite.ParsedPacket)のうち、DIxの状態を示す List-like なオブジェクト di_state(デジタルインタフェースの状態)0番目の真偽値を判定しています。di_stateの各値は、Trueのときに Low を示します。

データの待機

        twelite.daemon = True
        twelite.start()
        print("Started receiving")
        while True:
            twelite.join(0.5)

ここでは、別のスレッドで親機からのデータの待機を行っています。

Tweliteのオブジェクトは threading.Thread のサブクラスであり、ここではその機能によってデーモンスレッドを立ち上げ、スクリプトを終了するまでメインスレッドをブロックしています。

終了処理

        print("Flushing...")
        twelite.stop()
        print("Completed")

Ctrl-C が押された際には、twelite.stop()を呼び出すことでサブスレッドを終了しています。

Python スクリプトによる送信

次は、反対に TWELITE DIP のDO1へ接続された LED をPCから点滅させる Python スクリプトを実装してみます。

サンプルスクリプトの実行

下記の内容のスクリプト dip_blink_led.py を作成してください。

# -*- coding:utf-8 -*-
# TWELITE DIP start guide: blinking from python

from time import sleep

import mwings as mw


def main():
    twelite = mw.Twelite(mw.utils.ask_user_for_port())

    initial = {
        "destination_logical_id": 0x78,
        "di_to_change": [True, False, False, False],
        "di_state": [False, False, False, False],
    }
    command = mw.serializers.app_twelite.Command(**initial)

    while True:
        command.di_state[0] = not command.di_state[0]
        twelite.send(command)
        print(f"Flip DO1: {command.di_state[0]}")
        sleep(1)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("...Aborting")

スクリプトを実行すると、子機のDO1が一秒おきに点滅します。

実行時に複数の TWELITE R シリーズや MONOSTICK シリーズが接続されているときは、親機へ接続されたシリアルポートを選択してください。


poetry run python dip_blink_led.py
Multiple ports detected.
[1] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
[2] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
Select [1-2]: 2
Selected: /dev/cu.usbserial-R2xxxxxx
Flip DO1: True
Flip DO1: False
Flip DO1: True
Flip DO1: False
Flip DO1: True
^C...Aborting

サンプルスクリプトの解説

MWings ライブラリのインポート

import mwings as mw

短縮名 mw を使って呼び出せるようにしています。pandas as pdnumpy as np と同様です。

オブジェクトの作成

    twelite = mw.Twelite(mw.utils.ask_user_for_port())

親機をとやりとりするためのインタフェースとなる Twelite オブジェクトを作成しています。

引数にはシリアルポートを指定しますが、ここでは mw.utils.ask_users_for_port() ユーティリティを呼び出し、動的に決定しています。この関数はシリアルポートが存在しないときにエラーメッセージを出力し、1つだけ存在する場合はそのポートを、複数存在する場合はユーザが指定したポートを返します。

親機へ送信するコマンドの作成

    initial = {
        "destination_logical_id": 0x78,
        "di_to_change": [True, False, False, False],
        "di_state": [False, False, False, False],
    }
    command = mw.serializers.app_twelite.Command(**initial)

超簡単!標準アプリの子機へ送信するパケットを生成するためのコマンドを表すデータ commandmwings.serializers.app_twelite.Command)を初期化しています。

辞書オブジェクト initial は、コマンドの初期状態を表しています。ここでは、送信先の論理デバイスIDを 0x78(全子機)としたうえで、DI1を変更対象、High 状態としています。

データの送信

    while True:
        command.di_state[0] = not command.di_state[0]
        twelite.send(command)
        print(f"Flip DO1: {command.di_state[0]}")
        sleep(1)

ここでは、コマンドデータの文字列への変換および親機への送信を1秒おきに行っています。

コマンドデータのうち、DOxの状態を示す List-like なオブジェクト di_state(デジタルインタフェースの状態)0番目の真偽値を反転させることで、点滅を実現しています。