質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

4057閲覧

tkinterを使って、root.after(10, readSerial)によってシリアル信号をサイクリックに受け取る

HiroPokeHero

総合スコア45

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/08/15 01:28

編集2019/08/18 03:26

実現したいこと

Pythonを使ってArduino(CANシールド搭載)とシリアル通信をして
別のArduino(CANシールド搭載)と通信をしたい。

その際に
tkinterを使って、root.after(10, readSerial)によって
シリアル信号の受信を待ち続けて、
アプリケーションのテキストウィンドウに表示したい。

前提

両社のArduinoはすでにCAN通信が相互にできていることを確認できている。
また、単純にArduinoIDEのシリアルモニタで通信の確認もできている。

↓シリアルモニタ

=> Message Sent Successfully! Sending from CAN No.1 => Message Sent Successfully! Sending from CAN No.2 => Message Sent Successfully! Sending from CAN No.3

さらに、単純な以下のシリアル受信スクリプトでは正しく受信できている。

import serial ser = serial.Serial('COM3', 9600, timeout=0.1) print('Start') a = True i = 0 while a: data = ser.readline() print(data) i = i + 1 if (i>100): a = False ser.close()

結果

b'' b'Sending from CAN No.1\r\n' b' => Message Sent Successfully!\r\n' b'Sending from CAN No.2\r\n' b' => Message Sent Successfully!\r\n' b'Sending from CAN No.3\r\n'

やったこと

以下のようなコードを作成し、実行しました。
現時点では、テキストボックスではなく、シェルへの出力にしています。

import tkinter as tk from tkinter import ttk import serial ser = serial.Serial('COM3', 9600, timeout=0, writeTimeout=0) ser.close() def readSerial(): ser = serial.Serial('COM3', 9600, timeout=0, writeTimeout=10) while True: rxData = ser.read_all() rxData2 = rxData.strip().decode('utf-8') print(rxData2) txt.insert('end', rxData2) if rxData[:2] == "rx": txt.insert('end', rxData2) if len(rxData) == 0: ser.close() break root.after(10, readSerial) def trans1(): sigID = entryBox10.get() sigDLC = entryBox11.get() sigData = [] value = "ID:" + sigID + ", DLC:" + sigDLC +", Data:" sigData.append(entryBox12.get()) sigData.append(entryBox13.get()) sigData.append(entryBox14.get()) sigData.append(entryBox15.get()) sigData.append(entryBox16.get()) sigData.append(entryBox17.get()) sigData.append(entryBox18.get()) sigData.append(entryBox19.get()) value = "ID : " + sigID + ", DLC : " + sigDLC +", Data :" for i in range(int(sigDLC)): value = value + " " + sigData[i] value = value + "\n" #label11["text"] = value txt.insert('end', value) ser = serial.Serial('COM3', 9600, timeout=0.1) #ser.write(value) ser.write(b"tx:130h,8,FE,F3,33,44,44,22,35,22\n") ser.close() def trans2(): sigID = entryBox20.get() sigDLC = entryBox21.get() sigData = [] value = "ID:" + sigID + ", DLC:" + sigDLC +", Data:" sigData.append(entryBox22.get()) sigData.append(entryBox23.get()) sigData.append(entryBox24.get()) sigData.append(entryBox25.get()) sigData.append(entryBox26.get()) sigData.append(entryBox27.get()) sigData.append(entryBox28.get()) sigData.append(entryBox29.get()) value = "ID : " + sigID + ", DLC : " + sigDLC +", Data :" for i in range(int(sigDLC)): value = value + " " + sigData[i] value = value + "\n" #label11["text"] = value txt.insert('end', value) ser = serial.Serial('COM3', 9600, timeout=0.1) ser.write(value.encode('utf-8')) ser.close() # メインウィンドウ作成 root = tk.Tk() #メインウィンドウのタイトルを変更 root.title("CAN tool for CAN box 3") #メインウィンドウを640x480にする root.geometry("640x480") #ラベルを追加 label1 = tk.Label(root, text="受信内容"+" Don't Input anything here") label1.place(x=20, y=20) #テキストボックス txt = tk.Text(root) #txt.insert(1.0, "Don't Input anything here") txt.place(x = 20, y =40, height = 220, width = 600) #ラベルを追加 label2 = tk.Label(root, text="送信内容") label2.place(x=20, y=280) #送信信号1 y_sig1 = 300 entryBox10 = tk.Entry(root, width = 10) entryBox10.insert(tk.END, "140") entryBox10.place(x=20, y=y_sig1) label11 = tk.Label(root, text="h") label11.place(x=80, y=y_sig1) entryBox11 = tk.Entry(root, width = 2) entryBox11.insert(tk.END, "8") entryBox11.place(x=120, y=y_sig1) entryBox12 = tk.Entry(root, width = 6) entryBox12.insert(tk.END, "FF") entryBox12.place(x=150, y=y_sig1) entryBox13 = tk.Entry(root, width = 6) entryBox13.insert(tk.END, "FF") entryBox13.place(x=200, y=y_sig1) entryBox14 = tk.Entry(root, width = 6) entryBox14.insert(tk.END, "FF") entryBox14.place(x=250, y=y_sig1) entryBox15 = tk.Entry(root, width = 6) entryBox15.insert(tk.END, "FF") entryBox15.place(x=300, y=y_sig1) entryBox16 = tk.Entry(root, width = 6) entryBox16.insert(tk.END, "FF") entryBox16.place(x=350, y=y_sig1) entryBox17 = tk.Entry(root, width = 6) entryBox17.insert(tk.END, "FF") entryBox17.place(x=400, y=y_sig1) entryBox18 = tk.Entry(root, width = 6) entryBox18.insert(tk.END, "FF") entryBox18.place(x=450, y=y_sig1) entryBox19 = tk.Entry(root, width = 6) entryBox19.insert(tk.END, "FF") entryBox19.place(x=500, y=y_sig1) #ボタンを作る button1 = tk.Button(root, text="送信", command=trans1) button1.place(x=560, y=y_sig1-5) #送信信号2 y_sig2 = 340 entryBox20 = tk.Entry(root, width = 10) entryBox20.insert(tk.END, "141") entryBox20.place(x=20, y=y_sig2) labe21 = tk.Label(root, text="h") labe21.place(x=80, y=y_sig2) entryBox21 = tk.Entry(root, width = 2) entryBox21.insert(tk.END, "8") entryBox21.place(x=120, y=y_sig2) entryBox22 = tk.Entry(root, width = 6) entryBox22.insert(tk.END, "FF") entryBox22.place(x=150, y=y_sig2) entryBox23 = tk.Entry(root, width = 6) entryBox23.insert(tk.END, "FF") entryBox23.place(x=200, y=y_sig2) entryBox24 = tk.Entry(root, width = 6) entryBox24.insert(tk.END, "FF") entryBox24.place(x=250, y=y_sig2) entryBox25 = tk.Entry(root, width = 6) entryBox25.insert(tk.END, "FF") entryBox25.place(x=300, y=y_sig2) entryBox26 = tk.Entry(root, width = 6) entryBox26.insert(tk.END, "FF") entryBox26.place(x=350, y=y_sig2) entryBox27 = tk.Entry(root, width = 6) entryBox27.insert(tk.END, "FF") entryBox27.place(x=400, y=y_sig2) entryBox28 = tk.Entry(root, width = 6) entryBox28.insert(tk.END, "FF") entryBox28.place(x=450, y=y_sig2) entryBox29 = tk.Entry(root, width = 6) entryBox29.insert(tk.END, "FF") entryBox29.place(x=500, y=y_sig2) #ボタンを作る button2 = tk.Button(root, text="送信", command=trans2) button2.place(x=560, y=y_sig2-5) root.after(200, readSerial) #rootを表示し無限ループ root.mainloop()

発生している問題・エラーメッセージ

エラーはありません。

ただ、シェルに文字が出てきません。
ただ、改行はしているみたいです。
上記のプログラムでは画面がスクロールされている状態です。

スクリプト内部の出力を、print(rxData2)からprint(rxData)にすると
バイナリの何かしらを受信していることはわかります。
送信と受信のタイミングでしょうか?
※ちなみにシリアルのボーレートはすべて9600に統一していますので、
スクリプトの書き方が悪いように推測しています。

b'' b'' b'' b'' b''

試したこと

readline()を
read_all()、read()に変えてみる。
⇒効果なし

シリアル通信のタイムアウトを0から10に変更
⇒効果なし

補足情報(FW/ツールのバージョンなど)

Windows10、
Arduino1.8.9
Python.Python.3.7.4

追記8/17

現在のスクリプトを記載します。
ボタンが2つあって、
1つがstartSerial()とstopSerial()を割り当てています。
startSerial()によって受信開始(とうより読み込み開始?)して、
出力。
その途中にstopSerial()のボタンを押すと中断するようにしたい。

しかし、現状では受信開始してしまうとコントロールができなくなり、
ボタンを押すことができません。

ser = serial.Serial('COM3', 9600, timeout=0.1, writeTimeout=0) def readSerial(figS): global ser, fig print("a") ''' i = 0 b = True ser = serial.Serial('COM3', 9600, timeout=0.1, writeTimeout=0) while b == True: data = ser.readall() print(data) i = i + 1 if (i>100): b = False ser.close() ''' #ser = serial.Serial('COM3', 9600, timeout=0.1, writeTimeout=0) if flgS == True: while True: rxData = ser.readall() rxData2 = rxData.strip().decode('utf-8') print(rxData) if len(rxData) > 0: txt.insert('end', rxData2) break root.after(10, readSerial(True)) def startSerial(): print("Start serial") #ser = serial.Serial('COM3', 9600, timeout=0.1, writeTimeout=0) readSerial(True) def stopSerial(): print("Stop serial") #ser.close() readSerial(False)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mokemokechicken

2019/08/15 01:34

エラーが無いということはわかりましたが、現在はどういう状況なのでしょうか?
HiroPokeHero

2019/08/15 01:44

おっしゃる通りですね。 現在は受信ができていません。 本文に反映させますね。
guest

回答3

0

ベストアンサー

ser = serial.Serial('COM3', 9600, timeout=0, writeTimeout=10)

の部分ですが、
https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial
によると、
timeout=0 は NonBlocking Modeということですが、
それまでの検証では timeout=0.1 としていたので、ここもそうしたほうが良いのではないでしょうか。
※ だいぶ、意味が変わってきそうです
※ あと, writeTimeoutはあまり関係なから指定する必要はなさそうです。


上記API Referenceでは Serial#read_all() というメソッドは無いのようですが、
PySerialのVersionは何を使っていますでしょうか?
※ たぶん、あまり関係のないことだとは思いますが、念の為。

投稿2019/08/15 02:00

mokemokechicken

総合スコア948

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HiroPokeHero

2019/08/15 02:45

いろいろ試してみました。 これらでは効果がありませんでした(同じ症状) ser = serial.Serial('COM3', 9600, timeout=0, writeTimeout=0) ser = serial.Serial('COM3', 9600, timeout=10, writeTimeout=0) ser = serial.Serial('COM3', 9600, timeout=0.1, writeTimeout=0)
mokemokechicken

2019/08/15 05:20

そうですか。。。 ちなみに > 単純な以下のシリアル受信スクリプトでは正しく受信できている のスクリプトをそのまま root.mainloop() の直前に書く(1000回くらいのループで自動で抜けるとして)と、やはりちゃんと表示されるのでしょうか?
HiroPokeHero

2019/08/17 02:45 編集

まず、GUIをつかわない場合です。 ””” Start b'' b'' b'' b'' b'' b'' b'' b'' b'' b'' b'' b'Begin CAN Training => Entering Configuration Mode Successful!\r\n' b'Setting Baudrate Successful!\r\n' b'MCP2515 No.1 Initialized Successfully!\r\n' b'[Start ....]\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F3,80,01,4F\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'Sending from CAN No.3\r\n' b' => Message Sent Successfully!\r\n' b'rx:100h,4,F3,60,01,7C\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F3,60,01,51\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:101h,6,FD,08,FF,08,42,2C\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:102h,6,00,0C,FE,A6,FF,69\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F3,80,01,5C\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'Sending from CAN No.3\r\n' b' => Message Sent Successfully!\r\n' b'rx:100h,4,F3,80,01,73\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F3,80,01,5F\r\n' b'\r\n' ””” 次に、GUIから呼び出した場合です。 かなり、近い動作になります。 ””” Start serial a b'' b'' b'' b'' b'' b'' b'' b'' b'' b'' b'' b'Begin CAN Training => Entering Configuration Mode Successful!\r\n' b'Setting Baudrate Successful!\r\n' b'MCP2515 No.1 Initialized Successfully!\r\n' b'[Start ....]\r\n' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F4,40,01,50\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'Sending from CAN No.3\r\n' b' => Message Sent Successfully!\r\n' b'rx:100h,4,F4,30,01,6D\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F4,20,01,50\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:101h,6,FD,54,FF,54,42,C8\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:102h,6,FF,E9,FE,BE,FF,6B\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F4,30,01,55\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'Sending from CAN No.3\r\n' b' => Message Sent Successfully!\r\n' b'rx:100h,4,F4,10,01,81\r\n' b'\r\n' b'' b'' b'' b'' b'' b'' b'' b'\r\n' b'rx:100h,4,F4,20,01,53\r\n' b'\r\n' b'' Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\浩和\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__ return self.func(*args) File "C:\Users\浩和\Documents\Python\CANtool_CANbox3.py", line 39, in startSerial readSerial() File "C:\Users\浩和\Documents\Python\CANtool_CANbox3.py", line 12, in readSerial data = ser.readline() File "C:\Users\浩和\AppData\Local\Programs\Python\Python37\lib\site-packages\serial\serialwin32.py", line 267, in read raise portNotOpenError serial.serialutil.SerialException: Attempting to use a port that is not open ”””
HiroPokeHero

2019/08/16 11:07

この場合コントロールを完全に奪われてGUIを落とすしかシリアル通信を止めることができません。
guest

0

解決はしていませんが、状況が変わったのでスレッドを新しくします。

投稿2019/08/18 14:54

HiroPokeHero

総合スコア45

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ざっと見ただけだけど、

root.after(200, readSerial)

これでreadserialが実際に実行されてるか見てみることだけど、
そこの関数でなにかprintさせてみればどうでしょう


rxData = ser.read_all()

ここで、受信データがない場合、以降の処理をスキップさせないとダメでしょうね

それともひとつ、
その時点で受信できたデータを返す、ので、受信データはここでぶつ切りに受信されます
なので、受信データを溜め込んでいって、データの区切り(この場合は改行?)を検出して、そこで初めて受信データ群を以降の処理に回す、ということをしなくてはいけません。

投稿2019/08/15 01:55

編集2019/08/15 02:07
y_waiwai

総合スコア87782

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HiroPokeHero

2019/08/15 02:49

root.after(200, readSerial) でreadSerialが実行されていると思います。 readSerial()のなかでprint("a")を追加しました。 すると以下のようになりました。 a b'' a b'' a b'' 後半についてはもう少し詳しく教えていただけませんでしょうか?
y_waiwai

2019/08/15 02:53

受信データがない場合に、どういう動作になるか考えてみよう。 まさにあなたが提示したような動作になるんじゃないの?
HiroPokeHero

2019/08/15 02:57

その通りです。 受信データがない場合はこんな感じでOKです。 では、受信データがある場合は? 受信データがあるはずなのに、受信データがないかのような状態になっているので困っています。
y_waiwai

2019/08/15 03:01

受信データが有るときにはあるということに気がついてないだけのはなしでは。 read_allという関数がどういうもんかはわかりませんが、 おそらく、1バイトづつ受信されてるんでしょうね。 ログが流れているため、その受信したバイトを見れないでいるだけじゃないですかね
HiroPokeHero

2019/08/15 07:30

恐らくそうなんだと思います。 データが入ってきても読み込みをしたタイミングがフィットしなかったからだと思います。 ではどうすればいいのか? その方法がわかりません。 while文を回そうとすると 解決もしないし、 ツールの操作が効かなくなってしまいます。 流れているログを拾うことはできないでしょうか? ser.readline()を実行すると1行分を読むことが出きます。
y_waiwai

2019/08/15 07:32

だから、受信データがないときはログを出力するとかそういうのはパスするようにしましょうよ。 また、メッセージが1行分揃うまでは中で溜めておきましょう。
HiroPokeHero

2019/08/15 08:55

つまり、どうすればよいでしょうか? その方法がわかればいいのですが。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問