LoginSignup
3
4

More than 1 year has passed since last update.

シリアル通信ソフトを作成した

Posted at

欲しい! 簡易シリアル通信アプリ

 マイコンで装置を製作するとする、例えばXIAO RP2040 マイコンをMIcro pythonで複数のステップモータへの指令パルス発生装置を製作するとする。
 シリアル通信コマンドを受けて動作を行うプログラムを作成する時に、コマンドを送信して動作確認を行いたい。または、装置にコマンドを送って動作させたい。
 シリアルコマンドを送信するために、その昔は、秀Term、TeraTerm、PuTTYなどを使用していたが、近頃はArduino IDEのシリアル通信アプリが使い良いので、もっぱら使用してる。
 Arduino IDE並に軽いTermアプリがないものか、探してみたが、ない。

 簡単にシリアル通信コマンドを送信するもうひとつの方法は、PythonでPyserialを使ったプログラムを作って通信を行うやり方だ。これだとマイコン用のpythonエディタThonnyと別にpythonエディタを開いて、両方のプログラムをいじるという煩雑さがある。

 そこで、Arduino IDE並に簡単な汎用のTearmアプリをPython、Pyserial、Tkinterを駆使して作ることにした。2023年1月25日から始めて1月27日までに、まあまあのTermアプリに整ったので、御披露します。

簡易シリアル通信アプリ F.Term

まずは、ソースプログラムを掲載する。コメント行を含めて 270行 あまりで出来ました。

F.Term
#-------------------------------------------------------------------------------
# Name:        Term230125
# Purpose:     serial comunication
#
# Author:      T.F.
#
# Created:     25/01/2023
# Copyright:   (c) T.F. 2023
# Licence:     <your licence>
#
# Ver1.0        2023/01/25    ScrolledText
# Ver1.1        2023/01/26    add Button,ConboBox
#-------------------------------------------------------------------------------
# How to turn off overwrite mode in text editor
# Windows: [Fn] + [Insert]
#
#==================================================
# import
#==================================================
#import tkinter
from tkinter import *
import tkinter as tk

from tkinter import scrolledtext
from tkinter import messagebox

from tkinter import ttk

import serial
from serial.tools import list_ports

#==================================================
# text_area -> Enter key -> send
#==================================================
def send(event):
    global text_area,ser

    index = text_area.index(tk.END)
    print(f"cursor = {index}",type(index))
    #-----------------------------------------
    dot = index.find('.')
    print(f"dot = {dot}",type(dot))
    #-----------------------------------------
    ide = int(index[0:dot])
    print(f"ide = {ide}",type(ide))
    #-----------------------------------------
    ids = str(ide-1)+'.0'
    ide = str(ide)+'.0'
    #-----------------------------------------
    # input_text = text_area.get('1.0','2.0') #1行目0番から2行目0番までの文字列
    #-----------------------------------------
    input_text = text_area.get(ids,ide)       #ids行目0番からide行目0番までの文字列
    print(f"send_text = {input_text}",type(input_text))

    #-----------------------------------------
    # serial communication
    #-----------------------------------------
    if ser.is_open:
##        ser.write((input_text + "\n").encode("utf-8")) #改行:'\r'=CR' \n'=LF
        ser.write((input_text).encode("utf-8"))


#==================================================
# port open
#==================================================
def open(e,v):
    global ser

    #print('v=%s' % v.get())

    #--------------------------
    com = v.get()
##    print(com)
    #--------------------------
    ser.port = com
    ser.baudrate = 115200
    ser.timeout = 0.1
    #--------------------------
    try:
      ser.open()
      print("open serial is success!!")
      print(ser)
    except:
      print("error when opening serial")


#==================================================
# window close by win_X
#==================================================
def delete_window1():

    print("ウィンドウのxボタンが押された")
    try:
        # 終了確認のメッセージ表示
        ret = messagebox.askyesno(
            title = "終了確認",
            message = "プログラムを終了しますか?")

        if ret == True:
            # 「はい」がクリックされたとき
            win.destroy()
    except:
      print("error")

#==================================================
# window close by Btn2
#==================================================
def delete_window(e):

    print("ウィンドウのxボタンが押された")
    try:
        # 終了確認のメッセージ表示
        ret = messagebox.askyesno(
            title = "終了確認",
            message = "プログラムを終了しますか?")

        if ret == True:
            # 「はい」がクリックされたとき
            win.destroy()
    except:
      print("error")

#==================================================
#
# ここに定期的に実行する処理を記述する
#==================================================
def loop():
    global win, text_area, ser

    #----------------------------------------------
    # rec -> scrolledtext
    #----------------------------------------------
    rec=[]
    if ser.is_open:
##        rec = ser.read_all()
        rec = ser.readline()   # read to \n
        rec = rec.strip().decode('utf-8')

    if len(rec) != 0:
        text_area.insert(tk.INSERT, rec + '\n')
        text_area.see(text_area.index(tk.END))


    #----------------------------------------------
    win.after(100, loop)

#==================================================
# Tkinter
#==================================================
def Panel():

    global ports,win,text_area

    #-------------------------------------------------------------
    # root  base
    #-------------------------------------------------------------
    win = tk.Tk()
    win.title("F_Term")
    win.geometry("500x450")

    #-------------------------------------------------------------
    # frame
    #--------------------------------------------------
    #frame = tk.Frame(win)
    #-----------------------------
    #frame.pack()

    #-------------------------------------------------------------
    # Scrolledtext
    #-------------------------------------------------------------
    text_area = scrolledtext.ScrolledText(win,width=50,height=20,font=("Helvetica", 12, ""))
    text_area.bind('<Return>', send)
    #-----------------------------

    #-------------------------------------------------------------
    # Combobox
    #-------------------------------------------------------------
    v = StringVar()
    cb = ttk.Combobox(win, textvariable=v,values=ports, width=10)
    cb.set(ports[0])
##    cb.bind('<<ComboboxSelected>>',lambda e: print('v=%s' % v.get()))
    #-----------------------------

    #-------------------------------------------------------------
    # Button
    #-------------------------------------------------------------
    #button1 = ttk.Button(win, text='Open',command=lambda: print('v=%s' % v.get()))
    btn1 = ttk.Button(win, text='Open')
    btn2 = ttk.Button(win, text='Close',command=lambda: ser.close())
    btn3 = ttk.Button(win, text='終了')
    #---------------------------------------------
    btn1.bind("<Button-1>", lambda e:open(e,v))
    #btn2.bind("<Button-1>", lambda e:delete_window(e))
    btn3.bind("<Button-1>", lambda e:delete_window(e))

    #-------------------------------------------------------------
    # place
    #-------------------------------------------------------------
    W=90;H=30
    #------------------------------------
    cb.place(        x=20    ,y=10)
    btn1.place(      x=20+W  ,y=10)
    btn2.place(      x=20+W*2  ,y=10)
    btn3.place(      x=20+W*4  ,y=10)
    text_area.place( x=20    ,y=10+H)

    #------------------------------------
    # cursol
    #------------------------------------
    text_area.focus()
    e=0
    win.protocol("WM_DELETE_WINDOW", delete_window1)
    #------------------------------------
    # 100ms後にrepeat関数を実行
    #------------------------------------
    win.after(100, loop)

    #------------------------------------
    # END (loop start)
    #------------------------------------
    win.mainloop()


#==================================================
# S1.serch open serial port
#==================================================
##COMs = ['COM%s' % (i + 1) for i in range(256)]
##ports = []
###--------------------------------------------------
##for port in COMs:
##    try:
##        s = serial.Serial(port)
##        s.close()
##        ports.append(port)
##    except (OSError, serial.SerialException):
##        pass

#=======================================================================
# S2.serch open serial port
#=======================================================================
devices = list_ports.comports()             # ポートデバイスデータを取得
ports   = [info.device for info in devices] # ポート表示形式"COM1"に変換

#=======================================================================
# disp ports
#=======================================================================
if len(ports) == 0:
    print("error: device not found")
elif len(ports) == 1:
    print(f"only found {ports[0]}")
else:
    for i in range(len(devices)):
        print(f"input {i}: open {ports[i]}")

#==================================================
# シリアルポート定義
# ser = serial.Serial("COM21", 115200, timeout=0.1)
#==================================================
ser = serial.Serial()

#==================================================
# パネル表示
#==================================================
Panel()

#==================================================
# END  (after break loop)
#==================================================
ser.close()

print('program end')

解説

おいおい追記します。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4