from __future__ import annotations
import time
import serial
from serial import SerialException, SerialTimeoutException
PORT = "COM5"
BAUD_RATE = 115200
# Arduinoの100 msパルスとUSB通信時間を考慮した応答待ち時間
RESPONSE_TIMEOUT_SECONDS = 2.0
def send_trigger(ser: serial.Serial) -> None:
"""
ArduinoへTRIGコマンドを送り、ACKとDONEを待つ。
Raises:
RuntimeError:
ArduinoがBUSYまたはエラーを返した場合、
あるいは応答がタイムアウトした場合。
"""
# 前回の不要な受信データを破棄
ser.reset_input_buffer()
# Arduinoへ改行付きコマンドを送信
ser.write(b"TRIG\n")
ser.flush()
deadline = time.monotonic() + RESPONSE_TIMEOUT_SECONDS
ack_received = False
while time.monotonic() < deadline:
raw_line = ser.readline()
# timeout時間内に1行受信できなかった場合
if not raw_line:
continue
message = raw_line.decode("ascii", errors="replace").strip()
if not message:
continue
print(f"Arduino: {message}")
if message == "ACK":
ack_received = True
continue
if message == "DONE":
if not ack_received:
raise RuntimeError("DONEを受信しましたが、ACKを受信していません。")
return
if message == "BUSY":
raise RuntimeError("Arduinoは現在トリガー出力中です。")
if message.startswith("ERR"):
raise RuntimeError(f"Arduinoエラー: {message}")
raise RuntimeError("Arduinoからの応答がタイムアウトしました。")
def main() -> None:
try:
with serial.Serial(
port=PORT,
baudrate=BAUD_RATE,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=0.1,
write_timeout=1.0,
xonxoff=False,
rtscts=False,
dsrdtr=False,
) as ser:
print(f"{ser.port}を開きました。")
# 接続直後の安定待ち
time.sleep(0.2)
send_trigger(ser)
print("100 msトリガーが完了しました。")
except SerialTimeoutException as exc:
print(f"シリアル送信タイムアウト: {exc}")
except SerialException as exc:
print(f"シリアルポートエラー: {exc}")
except RuntimeError as exc:
print(f"通信エラー: {exc}")
if __name__ == "__main__":
main()