🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Python

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

3回答

9438閲覧

pySerial(Python)とSerial(Arduino)の読み書きの違い

退会済みユーザー

退会済みユーザー

総合スコア0

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Python

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2019/11/21 09:04

以前PythonとArduinoで文字列の送受信についていろいろ試していました。
しかし両者の読み書きに関する関数の違いについてあやふやな部分があるので、正確な答えを教えてください。いずれも2文字以上の文字列を読み書きする場合です。

今のところの認識は以下の通りです。

pySerial(Python)について

  • Serial.write(バイナリ文字列)

バイナリ文字列を1byteずつ全てポートに送信。
文字列をバイナリデータにしてから送信しなければならない。

  • Serial.readline()

ポートに届いている文字列を/nまで1byteずつ全て読み取る。
読み取った後デコードする必要がある。

Serial(Arduinno)について

  • Serial.write(文字列)

文字列をバイナリデータにして1byteずつ全てポートに送信

  • Serial.print(文字列)

文字列をASCII文字にして1byteずつ全てポートに送信

  • Serial.read()

ポートに届いている文字列を先頭の1文字だけ読み取る。
2文字以上読み取るときはwhile文やfor文を使う必要がある。

とくにSerial.read()以外の関数は**「関数内でシリアルに届いているデータを1byteずつ全て送信(または受信)する機能が元々備わっており、文字列の送受信が容易にできている」**ため複数文字列を送受信できるのだと思っているのですが間違っていますでしょうか?ならばなぜSerial.read()だけが1byteしか読み取れない関数なのですか?
他にもいろいろ間違っていればご指摘ください。

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

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

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

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

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

guest

回答3

0

ArduinoのSerialは、write(uint8_t), read()のそれぞれ1バイトの送受信を基本としています。

ただwriteに関しては関数のオーバーロードがなされてwrite("hoge")とするとwrite(const char *str)が呼び出され、文字列を複数バイトのデータとして送信できるようになっています。
read()はオーバーロードができないので、readBytesとかの別の名前のメソッドで、複数バイトのデータとして受信できるようになっています。

PythonのPySerial.serialは、writeとreadが基本で取り扱うデータの型はbytes(複数バイトを取り扱う型)です。
またPySerial.serialはio.RawIOBaseの派生でもあるので、その親のIOBase.readlineが使えるようになっており、動きもそちらのものを踏破しています。

後、文字列も利用する文字コードによりバイト変換した結果が、自分の想定したものと異なるものになる場合があると思います。たぶん注意するのはpython側かな。まあASCIIコードであれば特に問題なくやり取りできるかもしれませんが。

投稿2019/11/21 12:21

ta.fu

総合スコア1722

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

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

0

ベストアンサー

ネットワークの話だとしばしば「レイヤ(層)構造」の話になります。シリアル通信でも、単純ながらそういう話はあるわけで。

まず物理層。ここは非常に単純です。ここのデータ単位は8bitがひとかたまり=1byte(まぁ、8bit以外のデータも扱えますが99.9%は8bit)。それ以外の単位はありません。y_waiwaiさんが言っている「キャッチボール」はこのレイヤの話でしょう。送信側が1byteを送る。受信側はその1byteを受け取る。それだけです。ここに複数データの概念はありません。

その上に論理層を載っけて、複雑なデータをやりとりしていきます。
とはいえ、まずは送られてきたデータを単純に1byte受け取る機能が必要ですね。それがArduinoのSerial.read()であったり、Pythonでもread()関数があります。これらで、送信側が送った1byteのデータを受信することはまず出来るようになります。

さて、プロトコル、という言葉は聞いたことがありますか。なんと訳すかな、Google翻訳は「手順」とかいうのを第一に挙げてきましたけれど。1byteのデータを、ある規約の元にまとめ上げる、決まった手順で複数データを連ねて、「複数データを」やり取りできるようにするためのお約束を決めるのです。これは、送信側と受信側で合意があれば何でもいいとも言えますが、おおむねこんなことが決まっていればやり取り出来るでしょうか。

パターン1:常に決まった数のbyte数でひとつのデータを構成する。
パターン2:送信側は、これから送信するデータの量を最初に受信側に伝える。続いて、データ本体を送信する。
パターン3:データ本体に登場しないことが明らかなデータ値(またはデータ値のパターン)があるなら、そのデータ値までが一つのデータの塊とする。
これらの組み合わせもいろいろ考えられますが、基本的なところとしてはこんなものでしょう。

パターン1の例は、例えば先行するバイトデータに256を乗じて後発のバイトデータを足して一つのデータとする...と決めておけば、1byteのデータ2個で16ビットのデータをやりとり出来るようになります。もちろん、取り決め次第で後発のデータに256を乗じて先発のデータを足すようにしてもいいのですが。(重要なのは、「送る側と受ける側が同じ約束を共有している」ことです)

パターン2は...あまり表立って使われないかな、ちょっと例示はパス。

パターン3、これがまさにreadline()がやることです。例えば数字のデータの羅列であれば、データ中に改行コードは現れません。なので、改行データを区切りにしてやれば、改行が現れるまでのデータを「ひとかたまり」と認識出来ます。さらに、カンマと改行を組み合わせてデータを区切るCSV形式なんていうのもよく使われますね。

Serial.read()だけが1byteしか読み取れない

という考え方はこの階層構造を踏まえていません。全ての受信方法は、1byte受信の上に成り立っているのです。
Arduinoのシリアルライブラリはプリミティブ(原始的というか基本的というか)な機能しか備えていないので、1byte受信しか出来ません。複数データを組み合わせるような受信は、プログラマが作らなきゃいけません。
Pythonのライブラリは、1byte受信を積み重ねて複数のデータを取り扱う機能をライブラリに持たせている、ということです。

ついでに、「やってはいけないパターン」の話もちょっとだけ。
例えば、送信側で
'1' '2' '3'
を送ったとして、これをPythonのread_all()という関数で受け取ると
'1' '2' '3'
というデータが得られる...かも知れません。ただ、これは実に信用ならない話でして、もしかして送信側がその前に
'8' '9' '0'
を送っていて、受信側がなにかの事情で受信に手間取っていたとすると、read_all()では
'0' '1' '2' '3'
が受信できてしまうかも知れません。あるいは、受信側が受信仕切る前に送信側が次の
'4' '5' '6'
を送ってしまったら、受信の際にはそれを拾ってしまって
'1' '2' '3' '4' '5'
をまとめて受信してしまうかも知れません。
さらには、タイミング次第ではそもそも
'1' '2'しか受信しなかった、('3'は次のタイミングでの受信になっちゃった)なんてことも起こりえます。

もちろん、通信の時間配分とかを送信側受信側それぞれ十分考慮して、そういうクロスオーバーが起こらないと信じられる状況ならそれもいいですけれど、普通にはそれを保証出来るかというとなかなか難しく、なのでデータ構造をしっかりと持たせて、それによってデータの区切りを決めないともしかして怪しい動きをするソフトが出来てしまうかもしれません。(これはシリアルよりもTCP/IPの通信でそういうプログラムを組む人がしばしばいて危険なのですが...)

投稿2019/11/24 02:42

thkana

総合スコア7703

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

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

0

キャッチボールするイメージで、1バイトづつデータが投げてこられる、と思ってください
投げてこられるデータがただのバイナリデータか文字列かってのは、受けてるだけではわかりません。
また、どこでそのデータが終わるか、ってのもわかりません。

ということで、データの終わりがわからない以上、文字列を受信する関数、というのは作ることはできません

まあ、送る方は文字列の文字数はわかってるので、文字列送信関数はかんたんに作れますね

投稿2019/11/21 09:13

編集2019/11/21 09:16
y_waiwai

総合スコア88038

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問