Tera Term依存者がRaspberry PiでUART通信を確認するのに困った話
Raspberry Pi OS とUART
本記事は、
結論をここに述べると、二点になります。
- 設定を頑張りましょう
-
を使いましょうVSCode
序
私の見聞きしたところによれば、ssh
接続)が主な活躍の場である一方、もう一つ、「シリアルモニター」としての役割も持ちます。
シリアルモニターは、
プログラミング言語でもシリアルモニターの代用は可能ですが、当然乍らプログラムを書かなければなりません。加えて、受信する際には逐一データを取得して表示させなければならず、ただ人間が閲覧する用途に於いては不便です。
以前、
無線でデータ送信するには、
pyserial
で
今でこそ、シリアルモニターを使えばよいと分かりますが、当時の私は
本記事では、あれから知った代替案を紹介します。
Raspberry Pi の事前準備
シリアルモニターを紹介したとて、シリアル通信(
Raspberry Pi の設定
しばしば、
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
この通りとしか言いようがありませんが、一目見て分かりやすいのは、コードネームがbookworm
であることです。一つ前のコードネームはbullseye
でした。これより古い環境では手順が異なったり、画面内の項目が異なっていたりする場合があります。将来の新たな環境についても同様です。こればかりは、ご利用の環境に適った方法を探すしかありません。
設定→
インターフェイス→シリアルポートが無効になっている場合は、有効にする
上のスクリーンショットでは、「シリアルポート」と「シリアルコンソール」の両方を有効にしています。この状態では、シリアルコンソールへのログインが有効になります。かなり雑に言うと、
これに伴い、プログラムでシリアル通信を行おうとする際、「権限」を要することになります。面倒な上、今回はシリアルコンソールでログインする用事もありませんから、シリアルコンソールを無効にしてみます。
シリアルコンソールのみ無効にする
設定に変更がある場合は再起動することになります。
少し補足
上のような画面上の手順ではなく、コマンドsudo raspi-config
によって設定することがあります。特に、デスクトップがない状況下(ssh
ログイン時や
上下キーで3 Interface Options
を選択し、エンターキーで決定します。
Raspberry Pi 4 Model B Rev 1.5, 8GB
┌─────────────────────────────┤ Raspberry Pi Software Configuration Tool (raspi-config) ├──────────────────────────────┐
│ │
│ 1 System Options Configure system settings │
│ 2 Display Options Configure display settings │
│ 3 Interface Options Configure connections to peripherals │
│ 4 Performance Options Configure performance settings │
│ 5 Localisation Options Configure language and regional settings │
│ 6 Advanced Options Configure advanced settings │
│ 8 Update Update this tool to the latest version │
│ 9 About raspi-config Information about this configuration tool │
│ │
│ │
│ │
│ │
│ │
│ <Select> <Finish> │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
I6 Serial Port
を選択。
┌─────────────────────────────┤ Raspberry Pi Software Configuration Tool (raspi-config) ├──────────────────────────────┐
│ │
│ I1 SSH Enable/disable remote command line access using SSH │
│ I2 RPi Connect Enable/disable Raspberry Pi Connect │
│ I3 VNC Enable/disable graphical remote desktop access │
│ I4 SPI Enable/disable automatic loading of SPI kernel module │
│ I5 I2C Enable/disable automatic loading of I2C kernel module │
│ I6 Serial Port Enable/disable shell messages on the serial connection │
│ I7 1-Wire Enable/disable one-wire interface │
│ I8 Remote GPIO Enable/disable remote access to GPIO pins │
│ │
│ │
│ │
│ │
│ │
│ <Select> <Back> │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
ログインシェルについて問われます。先に触れたシリアルコンソールへのログイン(明確に決まった呼称はない?)の話です。ネットワークが無くとも、物理的に線で接続することで、
┌──────────────────────────────────────────────────────────┐
│ │
│ Would you like a login shell to be accessible over │
│ serial? │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ <はい> <いいえ> │
│ │
└──────────────────────────────────────────────────────────┘
いいえ
を選択した場合、シリアルポートについて問われます。こちらも無効にしてしまうと
┌──────────────────────────────────────────────────────────┐
│ │
│ Would you like the serial port hardware to be enabled? │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ <はい> <いいえ> │
│ │
└──────────────────────────────────────────────────────────┘
設定した内容が表示されます。
┌──────────────────────────────────────────────────────────┐
│ │
│ The serial login shell is disabled │
│ The serial interface is enabled │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ <了解> │
│ │
└──────────────────────────────────────────────────────────┘
設定が終ると、始めの画面に戻ります。
右キーでSelect
にカーソルが移動するので、左右でSelect
とFinish
を選択できるようになります。Finish
で設定画面を閉じます。
設定の確認
再起動後、設定した通り有効になったかどうか確認します。
視認
様々な情報がありますが、本記事では/dev/ttyS0
というシリアルポートを利用します。一応その探し方を示しますが、興味なければ飛ばしてください。
root
フォルダー(/
記号で表される)のもとにdev
フォルダーが存在します。
$ ls /
bin boot dev etc home ⋯ (中略)
この/dev
の中には、tty
というファイルが幾つも存在します。多すぎるので、ttyS
を含むものに絞り込みます。
$ ls /dev | grep ttyS
ttyS0
シリアルポートが有効になっていないと、ttyS*
は存在しないため、何も出てきません。これで、有効になったことを確認できます。
通信テスト
GPIO一覧(引用:https://deviceplus.jp/raspberrypi/raspberrypi-gpio/)
TXD
)が送信側、RXD
)が受信側です。単にこの二つのピンを接続することで、自分で送ったデータが自分に返ってきます。
配線できたら、
icecream
はpip install icecream
などでインストールできますが、無視してprint
に置換しても構いません。見好いと感ぜられたら是非お使いください。
pip installでエラーが出た場合
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
For more information visit http://rptl.io/venv
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
ある程度新しい環境であれば、このようなエラーが出るはずです。僭越乍ら、この対処を記事として御座います。
そうでなくとも、PEP 668
と検索すれば充分な情報が見つかります。
頓着なされないならば、pip install icecream --break-system-packages
でインストールできます。
import time
import serial
from icecream import ic
# シリアルポート
PORT = '/dev/ttyS0'
# ボーレート
BAUDRATE = 115200
# シリアル通信初期化
with ic(serial.Serial(PORT, BAUDRATE, timeout=0.5)) as uart:
ic(''' 改行コードが無い場合 ''')
# 送信
ic(uart.write(b'serial'))
# 一応待機
time.sleep(0.1)
# 受信
while ic(uart.read()):
# 一応待機
time.sleep(0.1)
ic(''' 改行コードを入れる場合 ''')
# 送信
ic(uart.write(b'serial\n'))
# 一応待機
time.sleep(0.1)
# 受信
ic(uart.readline())
御存じの通り、python ファイル名.py
で実行できます。
ic| serial.Serial(PORT, BAUDRATE, timeout=0.5): Serial<id=0x7f83dbe710, open=True>(port='/dev/ttyS0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=0.5, xonxoff=False, rtscts=False, dsrdtr=False)
ic| ' 改行コードが無い場合 '
ic| uart.write(b'serial'): 6
ic| uart.read(): b's'
ic| uart.read(): b'e'
ic| uart.read(): b'r'
ic| uart.read(): b'i'
ic| uart.read(): b'a'
ic| uart.read(): b'l'
ic| uart.read(): b''
ic| ' 改行コードを入れる場合 '
ic| uart.write(b'serial\n'): 7
ic| uart.readline(): b'serial
'
文字列ではなく、バイナリーデータ(バイト列とも)を送受信している点にご注意下さい。例に、'serial'
ではなく、b'serial'
となっています。
また、最後の行の表示がやや崩れおりますが、改行コード\n
によって改行されているだけです。
プログラム概説と余談
詳細な説明は致しませんが、簡単に概要に触れておきます。なおic
はデバッグ用についているものですので、以下には省略しています。
初期化については、シリアルポートを間違えないようにすれば(とりあえず)問題ありません。ボーレートは、よくある115200
という値を使用しています。本来は、制御対象のボーレートに合わせる必要があります。データシートを読みましょう。
with
は、
with serial.Serial('/dev/ttyS0', 115200, timeout=0.5) as uart:
...
次に同じ意味となります。
uart = serial.Serial('/dev/ttyS0', 115200, timeout=0.5)
...
uart.close()
with
を使った記述では、close()
を書く必要がなくなります。楽ですね。
プログラムを御覧の通り、二つの方法を使っています。これが全てではありませんが、参考までにご紹介します。
- 改行コードがない場合
個人的にこのやり方は推奨しませんが、単純なので使用しています。
uart.write(b'serial')
uart.read()
この場合、送信されるデータがb'serial'
であるのに対し、受信されるデータは、一文字目(1バイト目)のb's'
のみであります。今一度、上の実行結果を見ると、一文字ずつ表示されていることに納得頂けるでしょう。
次のようにすることで、一応全てのデータを受け取ることができています。
while uart.read():
...
uart.read()
が「データを受信している間」、この処理が繰り返されるようになっています。
然らば「データを受信しなかった場合」、どうなるでしょうか。
serial.Serial(PORT, BAUDRATE, timeout=0.5)
timeout=0.5
とあるのは、「0.5
秒間データを受信しなかった場合」タイムアウトとし、受信を止めるようにするものです。timeout
を指定しないと、データを受信するまで、頑として処理の流れを防禦するため、いつまで経ってもプログラムが終わらなくなることがあります。
「終わられると困る場合」は指定せず、「終わらないと困る場合」は指定することになります。
- 改行コードがある場合
改行コードがある場合、行単位で一挙にデータを受信できるようになります。
uart.write(b'serial\n')
uart.readline()
readline()
は、改行コードが見つかるまでデータ受信を続けます。結果として、serial
という文字列の連結を保持したまま受信できています。基本的にはこちらの方が便利ではないでしょうか。
蛇足になりますが、画像のようなバイナリーデータを扱う際には不都合を生じます。
画像によっては、改行コードと同じ値が混入します。readline()
は改行コードが見つかるまで勝手に受信してくれる一方、改行コードが見つかると勝手に受信を終えてしまいます。
>>> uart = serial.Serial(PORT, BAUDRATE, timeout=0.5)
>>> uart.write(b'\x11\x22\n\x33\x44')
5
>>> uart.readline()
b'\x11"\n'
>>> uart.readline()
b'3D'
>>> uart.readline()
b''
b'\x11\x22\n\x33\x44'
というバイナリーデータを送信し、readline()
で受信します。\x11
を除いて表示が変化していますが、文字コードとして解釈され、文字に変換されているに過ぎません。
>>> b'\x21'
b'!'
>>> b'\x22'
b'"'
>>> b'\x23'
b'#'
>>> b'\x32'
b'2'
>>> b'\x33'
b'3'
>>> b'\x34'
b'4'
>>> b'\x43'
b'C'
>>> b'\x44'
b'D'
>>> b'\x45'
b'E'
改行コードによる中断を防ぎ、一挙に受信したい際は、readlines()
を使うのが簡単です。
>>> uart.write(b'\x11\x22\n\x33\x44')
5
>>> uart.readlines()
[b'\x11"\n', b'3D']
以上、
本題
ここまで来て漸く、シリアルモニターを考える段階に至りました。
Arduino IDE
先んじて、需要の狭いものから済ませましょう。
シリアルモニターは
現状、
もう一つは、
Legacy IDE
sudo apt install arduino
ただ、「ボード選択」をしていない状態では、シリアルモニターを開けないようでした。
ボードnullは利用できません
と出てきます。本来はボードを接続して使うものですから、接続せずにエラーが出るのは真っ当です。今は
Arduino IDE 2
一つは、
もう一つ、物々しい記事があります。私が調べた限りでは、唯一
上の情報に似た手順も示していますが、私はこちらも駄目でした。しかし、様子の異なる情報も含まれています。要するに、「提供されていないから自分で用意する」という話のようです。見ると分かる通り手順が煩雑ですから、シリアルモニターを使いたいがために征くべき道ではありません。
VSCode 拡張機能 Serial Monitor
sudo apt install code
インストールすると、メニューに追加されます。
メニュー→プログラミングの中に追加されている
日本語化も拡張機能のインストールで行います。
拡張機能のインストール
元が暗色なので明るく補正
画像のアイコンから、拡張機能のメニューが現れます。
検索窓でserial monitor
を探して、インストールします。
画面下にマウスポインターを遣ると、↕
に変化する箇所があります。その箇所から上に引っ張ると、コンソールの類が出てきます。
下部にマウスポインターを合わせ、↕
になる所で引っ張り上げる
この中にSERIAL MONITOR
があります。分かりにくいので、私はドラッグ&ドロップで左のメニューに移しています。これは好みです。
移動の様子
掴むと浮かび上がる
左のメニューに移動すると、白い線が出る
アイコンができて分かりやすい
どこにあるか分かりやすい一方で、縦長なので普通に使いづらいです。私は甘受しています。
動作確認
なんとなく
英名 | エスケープシーケンス | 備考 |
---|---|---|
|
\n |
|
|
\r |
曽ての |
\r\n |
|
監視の開始
をクリックすると、シリアルモニターによる監視ができるようになります。
下の窓に入力した内容を送信します。表示がおかしく見づらいですが、serial
と入力しています。
送信した内容と、受信した内容が表示されます。
監視の停止
で、シリアルモニターでの監視を閉じます。
シリアルモニターを自作する
最後に、趣味レベルのお話を付け加えます。案の定と言いましょうか、世の中には、シリアルモニターを自作している方々が既にいらっしゃるのです。
私も当初、
今は生成どうでもいいプログラムは作らせてしまうという選択肢があります。せっかくなので、当時の雪辱を果たすべく、誰もいらないと思うので、
また、上二つの参考記事が使用するpysimplegui
ですが、ライセンス形態を改めたことで一時騒がれました。商用利用について有償化しており、個人利用でもライセンス登録を要するとのことです。
ところで今回私が作らせたものは、tkinter
のみで構成しています。甚だ傲慢と存じますが、一応代替となるでしょう。また、若し「
source code
次の箇所は、python3.12
以降の表記です。
type SerialConnectionState = Serial | None
以前のバージョン(3.11
など)ではエラーになるので、type
を消します。
SerialConnectionState = Serial | None
以下は、後者に合わせました。
import threading
from tkinter import END, WORD, PhotoImage, Tk, Toplevel, messagebox, scrolledtext, ttk
from typing import Callable, Final
from serial.tools.list_ports_common import ListPortInfo
from serial import Serial, SerialException
from serial.tools import list_ports
# 接続状態型
# type SerialConnectionState = Serial | None
SerialConnectionState = Serial | None
class SerialApp:
''' シリアルモニター '''
def __init__(self):
''' GUIの構成とUARTの設定 '''
# initialize tk GUI
self.__root: Final[Tk] = Tk()
self.__root.title(
string = "UART Serial Monitor"
)
self.__root.iconphoto(
False,
PhotoImage(
file = 'src/icon.png'
)
)
# スレッド停止フラグ
self.__disconnect_flag = threading.Event()
# シリアル設定データ
self.__serial_settings = {
"port": None,
"baudrate": 115200
}
# 接続状態
self.__serial_connection: SerialConnectionState = None
# 設定画面を表示する
self.__open_settings_window()
# メインウィンドウのウィジェットを作成
self.__create_main_widgets()
def __del__(self):
if self.__serial_connection:
self.__serial_connection.close()
def mainloop(self):
''' メインウィンドウを起動する '''
self.__root.mainloop()
def __create_main_widgets(self):
''' メインウィンドウの画面構成 '''
# 設定ボタン
self.__settings_button = ttk.Button(
master = self.__root,
text = "設定",
command = self.__open_settings_window
)
self.__settings_button.grid(
row = 0,
column = 0,
padx = 5,
pady = 5
)
# 接続/切断ボタン
self.__connect_button = ttk.Button(
master = self.__root,
text = "接続",
command = self.__toggle_connection
)
self.__connect_button.grid(
row = 0,
column = 1,
padx = 5,
pady = 5
)
# 送信テキストボックス
self.__transmitt_label = ttk.Label(
master = self.__root,
text = "送信データ:"
)
self.__transmitt_label.grid(
row = 1,
column = 0,
padx = 5,
pady = 5,
sticky = "w"
)
self.__send_entry = ttk.Entry(
master = self.__root
)
self.__send_entry.grid(
row = 1,
column = 1,
columnspan = 3,
padx = 5,
pady = 5,
sticky = "we"
)
# 送信ボタン
self.__send_button = ttk.Button(
master = self.__root,
text = "送信",
command = self.__send_data
)
self.__send_button.grid(
row = 1,
column = 4,
padx = 5,
pady = 5
)
# ログ表示エリア
self.__log_area = scrolledtext.ScrolledText(
master = self.__root,
wrap = WORD,
state = "disabled",
height = 15
)
self.__log_area.grid(
row = 2,
column = 0,
columnspan = 5,
padx = 5,
pady = 5,
sticky = "nsew"
)
# レイアウト調整
self.__root.grid_columnconfigure(
index = 1,
weight = 1
)
self.__root.grid_rowconfigure(
index = 2,
weight = 1
)
def __open_settings_window(self):
""" ポート設定ウィンドウを開く """
PortSettingsWindow(
master = self.__root,
on_save_callback = self.__save_settings
)
def __save_settings(self, port: ListPortInfo, baudrate: int):
""" 設定を保存 """
# 状態保存
self.__serial_settings["port"] = port
self.__serial_settings["baudrate"] = baudrate
# 保存メッセージウィンドウ
messagebox.showinfo(
title = "ポート設定",
message = f"ポート: {port}\nボーレート: {baudrate}bps が保存されました。"
)
def __toggle_connection(self):
''' 接続状態切り替え '''
if self.__serial_connection:
self.__disconnect()
else:
self.__connect()
def __connect(self):
''' 接続 '''
# 切断中のみ実行する
if not self.__serial_connection:
# ポート設定篩
if not self.__serial_settings["port"]:
messagebox.showerror("エラー", "設定でポートを選択してください。")
return
try:
# 通信設定
self.__serial_connection: SerialConnectionState = Serial(
port = self.__serial_settings["port"],
baudrate = self.__serial_settings["baudrate"],
timeout = 1
)
# ボタンを接続から切断へ
self.__connect_button.config(
text = "切断"
)
# 接続メッセージ
self.__log_message(
message = f"接続しました: {self.__serial_settings['port']} @ {self.__serial_settings['baudrate']}bps"
)
# 接続状態 (disconnect: False)
self.__disconnect_flag.clear()
# シリアル入力タスク
self.__read_thread = threading.Thread(
target = self.__read_data,
daemon = True
)
self.__read_thread.start()
except SerialException as e:
messagebox.showerror(
title = "エラー",
message = f"接続できません: {e}"
)
def __disconnect(self):
''' 切断 '''
# 接続中のみ実行する
if self.__serial_connection:
# スレッド停止
self.__disconnect_flag.set()
# スレッドが完全に終了するのを待つ
self.__read_thread.join()
# シリアル接続を閉じる
self.__serial_connection.close()
# 接続状態を変更する
self.__serial_connection: SerialConnectionState = None
# ボタンを切断から接続へ
self.__connect_button.config(
text = "接続"
)
# 切断メッセージ
self.__log_message("切断しました。")
def __send_data(self):
''' シリアル出力 '''
# 接続中のみ実行する
if self.__serial_connection:
# 入力窓内容の取得
data: str = self.__send_entry.get()
if data:
# シリアル出力
self.__serial_connection.write(
data.encode()
)
# 送信メッセージ
self.__log_message(
message = f"送信: {data}"
)
else:
messagebox.showwarning(
title = "警告",
message = "接続されていません。"
)
def __read_data(self):
''' シリアル入力 '''
# 接続中のみ実行する
if self.__serial_connection:
while not self.__disconnect_flag.is_set():
try:
# シリアル入力を読み取る
if data := self.__serial_connection.readline().decode().strip():
# 受信メッセージ
self.__log_message(
message = f"受信: {data}"
)
except Exception as e:
self.__log_message(
message = f"受信エラー: {e}"
)
# 5ms待機
self.__disconnect_flag.wait(0.005)
def __log_message(self, message: str):
''' メッセージエリアへの表示 '''
# 可変
self.__log_area.config(
state = "normal"
)
# 表示
self.__log_area.insert(
index = END,
chars = f'{message}\n'
)
# 不変
self.__log_area.config(
state = "disabled"
)
# 可視
self.__log_area.see(
index = END
)
class PortSettingsWindow:
''' 設定画面 '''
def __init__(self, master: Tk, on_save_callback: Callable[[SerialApp, ListPortInfo, int], None]):
''' 設定と画面構成 '''
# 主体
self.master: Tk = master
# 設定保存時に呼び出すコールバック
self.on_save_callback = on_save_callback
# 画面構成
self.create_setting_widgets()
def create_setting_widgets(self):
''' 画面構成と有効ポートの取得 '''
# ウィンドウの設定
self.window = Toplevel(
master = self.master
)
self.window.title(
string = "ポート設定"
)
self.window.resizable(
width = False,
height = False
)
self.window.attributes(
"-topmost",
True
)
self.window.iconphoto(
False,
PhotoImage(
file = 'src/icon.png'
)
)
# ポート選択ラベルとコンボボックス
self.port_label = ttk.Label(
master = self.window,
text = "ポート:"
)
self.port_label.grid(
row = 0,
column = 0,
padx = 10,
pady = 10,
sticky = "w"
)
self.port_combobox = ttk.Combobox(
master = self.window,
state = "readonly"
)
self.port_combobox.grid(
row = 0,
column = 1,
padx = 10,
pady = 10
)
self.update_ports() # ポート一覧を更新
# ボーレート選択ラベルとコンボボックス
self.baudrate_label = ttk.Label(
master = self.window,
text = "ボーレート:"
)
self.baudrate_label.grid(
row = 1,
column = 0,
padx = 10,
pady = 10,
sticky = "w"
)
self.baudrate_combobox = ttk.Combobox(
master = self.window,
state = "readonly"
)
self.baudrate_combobox["values"] = [9600, 19200, 38400, 57600, 115200]
self.baudrate_combobox.current(
newindex = 4
)
self.baudrate_combobox.grid(
row = 1,
column = 1,
padx = 10,
pady = 10
)
# 保存ボタン
save_button = ttk.Button(
master = self.window,
text = "保存",
command = self.save_settings
)
save_button.grid(
row = 2,
column = 0,
columnspan = 2,
pady = 20
)
def update_ports(self):
""" シリアルポートの一覧を取得してコンボボックスに設定 """
# 有効シリアルポート一覧
ports = list_ports.comports(True)
port_names = [port.device for port in ports]
# コンボボックスを更新する
self.port_combobox["values"] = port_names
# 有効ポートがある場合
if port_names:
# 最初のポートを選択状態にする
self.port_combobox.current(0)
# 無い場合
else:
# 空のリストを設定
self.port_combobox["values"] = []
# 表示をクリア
self.port_combobox.set("")
messagebox.showwarning(
title = "警告:シリアルポート無効",
message = "利用可能なシリアルポートが見つかりません。",
parent = self.window
)
def save_settings(self):
""" 設定を保存し、コールバックを呼び出す """
# 選択されたものを取得する
selected_port = self.port_combobox.get()
selected_baudrate = self.baudrate_combobox.get()
if not selected_port:
messagebox.showerror(
title = "エラー",
message = "ポートを選択してください。",
parent = self.window
)
return
# 設定を保存するコールバックを呼び出す
self.on_save_callback(selected_port, int(selected_baudrate))
# 設定画面を閉じる
self.window.destroy()
from SerialApp import SerialApp
SerialApp().mainloop()
icon.png
実行の際はmain.py
を指定します。
python main.py
三つのファイルは全て同じフォルダー内に置きます。一応、構造に示しておきます。
$ tree
.
├── SerialApp.py
├── icon.png
└── main.py
1 directory, 3 files
実行の様子
設定画面(左)でシリアルポートを選ぶ
設定内容が表示される
接続する
送信(手動)と受信(自動)
畢竟
そもそも本記事で何を書こうとしていたのかというと、「
結論を明らかにすると、「
Discussion