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

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

ただいまの
回答率

87.48%

Python>Arduino>Pythonの相互シリアル通信

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,195

score 23

やりたいこと

Python>Arduino>Pythonの相互に文字列のやり取りがしたいです。
試しにPythonで作ったランダムな数字をArduinoで2分の1して、結果をPythonに返すプログラムを書いていますがうまくいきません。

送信する文字列は、 Python:"Pyランダム数字" > Arduino:"Ardランダム数字/2" > Python です。
※このあと複雑なやり取りに応用したいので、どうしても"Py ** "、 "Ard ** "の文字列で送受信したいです。

訳あってtkinterに結果を出力しています。
宜しくお願いします。

追記

追記:かなり編集したので最初にupしてたプログラムを削除しました。

簡易的なものを作ってみましたがうまくいきませんでした。
Python>Arduino>Pythonで文字列をシリアル通信でオウム返しするプログラムです。

受信側では1文字ずつ受け取るように変更し、
終端文字を改行コードにしたり、 . にしたりいろいろ試しました。

実行はできますが、ずっとループしているようで先に進めません。
Arduino側で  if (i > 10 || input[i] == '.') {   に引っかからない、
またはPython側で   if (line[-1] == '.') : break   に引っかからないのがネックなようです。

これでもダメなら何か他の方法を使ってみようと思います。

import serial
import time

i = 1

ser = serial.Serial('COMポート',9600,timeout=None)

while True:
    #送信
    data = str(i) + '.'
    flag=bytes(data,'utf-8')
    ser.write(flag)   #シリアル通信:送信

    time.sleep(0.3)  #必要か?
    line = 'null'    #whileとifで判定するための初期化

    #受信
    while line is 'null' :
        if (line[-1] == '.') : break
        line += ser.read()
    print(line)
    line = line.decode('utf-8')
    line = line.replace('null', '')
    line = line.replace('\r\n', '')
    print(line)
    i += 1
ser.close()
#include <stdio.h> 

char input[10];   // 文字列格納用
int i = 0;  // 文字数のカウンタ

void setup() {
  Serial.begin(9600);
}

void loop() {
  // データ受信したとき
  if (Serial.available()) {
    input[i] = Serial.read();
     // 文字数が10以上 or 末尾文字'.'
    if (i > 10 || input[i] == '.') {
      // 末尾に終端文字の挿入
      input[i] = '\0';
      Serial.write(input);  // 受信文字列を送信
      // カウンタの初期化
      i = 0;
    } else { i++; }
  }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • t_obara

    2019/08/28 10:36

    そもそもPythonとArduinoは同一に語るべきレイヤのものではないので意味不明です。
    AuduinoとはPCやラズパイ、IoTデバイスなどですよね。PythonとはC/C++などとなります。
    さて、今回のような課題に対して、一番やらなくてはいけないことは、どこに問題があることを切り分けることです。そのために、問題があると想定される部分を最小限にして確認していくことが重要となります。
    今回、送受信をそれぞれで確認していますが、まずは一般的なソフトを利用して送信されたデータが受信側で正常に受信されているかを確認するといったことが考えられます。

    キャンセル

回答 4

+2

本当は自己解決のプログラムについてのコメントなんですが、コードを含むので回答として書きます。

Arduino側

char input[30];     // 受信文字列格納
<略>
void loop() {
  if (Serial.available()) {
    input[i] = Serial.read();
     // 文字数が10以上 or 末尾文字'.'
    if (i > 30 || input[i] == '.') {
      // 末尾に終端文字の挿入
      input[i+1] = '\0';
      String tmp = input;         //replaceするために1度str型に
      tmp.replace("Py" , "Ard");
      tmp.toCharArray( sendata , tmp.length() ) ;   
      Serial.write(sendata);    //送信
      Serial.write(".\n");      //何故か受信した.が消える謎
      // カウンタの初期化
      i = 0;
    } else { i++; }
    // 受信文字列を送信
  }


このプログラムでは、i++でi==30またi==31になった後、次回のloop()の実行でi[30]およびi[31]に対して代入が行われます。また、i==31でif文の条件に入った時、input[i+1]つまりinput[32]に書き込みします。いわゆるバッファオーバーフローで、致命的なエラーと言えます。もちろん、'.'の受信がエラーになったときにしかそのモードにはならないので、なかなか発現しないとは思いますが、コードに書いてある以上はプログラマの責任としてちゃんと動くようにしましょう。配列のサイズと、制限する受信文字数の整合をとるべきです。

それと、何故か受信した.が消える謎なんていうのを放置すると、あとでしっぺ返しを喰らうことがあります。
まず、その時点で'.'がいないことについて
「受信した.が消えた」
「.が送信されていなかった」
「送信された.が受信されなかった」
「PC側に送り返した'.'が受信されていなかった」
等のモードが考えられますが、「受信した.が消えた」であることは確認されているのでしょうか?
少なくとも質問のプログラムでは

    if (i > 10 || input[i] == '.') {
      // 末尾に終端文字の挿入
      input[i] = '\0';


としていますから、'.'が入っていたinput[i]を'\0'で「消して」います。自己解決のプログラムでは対策されているのですが、この辺で打った手は整理されていますか?

t_obaraさんがコメントされているように、シリアルモニタやteratermなどを使用して、少なくともArduino側の通信動作は確認出来るはずです。これは是非やるべき。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/31 20:15

    ご指摘ありがとうございます!自分でも甘かったように思ってます。
    やっぱり実行できるだけではだめですよね。
    さらに改良しようと思います。

    キャンセル

check解決した方法

0

皆さんの意見を参考にしながら細かいところまでいじくってたらできました。
Python側でシリアルポートを開いた後、time.sleep(3)するのもミソだったようです。

しかし空の文字列が帰ってきているのか、受信漏れがあり、さらに改善していこうと思います。
ありがとうございました!

以下プログラムと実行結果

追記
<改良版(受信漏れなし)>
(Python) "Py(数字)" > (Arduino) "Ard(数字)" > (Python) 表示

import serial
import time

i = 1 #送信する文字
ser = serial.Serial('COMポート',9600,timeout=None)
time.sleep(3)
while True:
    #送信
    data = 'Py' + str(i) + '.'     #終端文字
    flag=bytes(data,'utf-8')
    ser.write(flag)
    print(data , flag)  #確認用

    time.sleep(1)  #必要か?
    line = 'null' 

    #受信
    while not ('.\n' in line):
        temp = ser.readline()
        temp = temp.decode('utf-8')
        line = line + temp
    line = line.replace('null', '')
    line = line.rstrip('\n')    #余分な改行は取り除く
    print(line)   #確認用

    i += 1

ser.close()
#include <stdio.h> 

char input[30];     // 受信文字列格納
char sendata[30];   // 送信文字列格納
int i = 0;          // 文字数のカウンタ

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) {
    input[i] = Serial.read();
     // 文字数が10以上 or 末尾文字'.'
    if (i > 30 || input[i] == '.') {
      // 末尾に終端文字の挿入
      input[i+1] = '\0';
      String tmp = input;         //replaceするために1度str型に
      tmp.replace("Py" , "Ard");
      tmp.toCharArray( sendata , tmp.length() ) ;   
      Serial.write(sendata);    //送信
      Serial.write(".\n");      //何故か受信した.が消える謎
      // カウンタの初期化
      i = 0;
    } else { i++; }
    // 受信文字列を送信
  }
}
Py1. b'Py1.'
Ard1.
Py2. b'Py2.'
Ard2.
Py3. b'Py3.'
Ard3.
Py4. b'Py4.'
Ard4.
Py5. b'Py5.'
Ard5.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

Arduino側で
serialReadが3重ループになってますけど、抜けてなくないですか?

またpython側でも
self.ser.write(flag)   #生成した数字を送信

この部分って30文字以上か終端文字送ってますか?
戻り値で何バイト送ったかわかるようなのでチェックしてみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

送信データに終端記号をつけましょう
テキストデータなら、改行(\n)ですね。

メッセージの終端がわからないため、やみくもに30文字受信しても、送信途中から受信してしまうとワケワカランデータとなってしまって破綻します。
受信側では、改行が来るまでデータを貯めていき、改行が来たら次の処理に回すようにします

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る