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

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

詳細はこちら
Python 3.x

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

シリアルポート

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

Arduino

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

Q&A

解決済

3回答

3095閲覧

arduinoのシリアル通信で0x00を受信したい

crafter_tarako

総合スコア4

Python 3.x

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

シリアルポート

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

Arduino

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

0グッド

0クリップ

投稿2019/11/29 15:15

問題になっていること

今python(pc)->arduinoで画像(128*64,白黒1bit/pixel)の通信機能を実装しています。
下のコードは、全てのドットを白で埋めた画像を送信したところ、不具合なく動作しました。
しかし、ほかの画像でバイト配列の中に0x0が入るとarduino側で受信できないのか処理してくれません。
0x0を受信できないことについて調べても情報がなかったので、詳しい方、どなたか原因を教えてください。

##ソースコード
念のため全文載せます。変数スペルミスたぶんあるけど無視してください。

python

1import serial 2import time 3from PIL import Image,ImageDraw,ImageFont 4import numpy as np 5 6im=Image.new("1",(128,64)) 7im_d=ImageDraw.Draw(im) 8im_d.font=ImageFont.truetype(r"O:\PixelMplus12-Regular.ttf",40) 9im_d.text((0,-5),r"テスト test",1) 10#im_d.polygon((0,0,0,64,128,64,128,0),"WHITE") 白埋め 11#im.show() 12im_ar=np.asarray(im) 13time.sleep(2) 14 15 16with serial.Serial('COM14',baudrate=115200,timeout=None,bytesize=8,parity="N",stopbits=1,dsrdtr=False) as ser: 17 time.sleep(3) 18 def packetsend(stat,data=[0]): 19 ser.write(stat.to_bytes(1,'big')) 20 21 for packet in data: 22 ser.write(packet.to_bytes(1,'big')) 23 24 ser.write(0x29.to_bytes(1,'big')) 25 ser.write(0x40.to_bytes(1,'big')) 26 return ser.readline() 27 28 packetsend(0x14) 29 time.sleep(2) 30 31 for im_snd in im_ar: 32 sendbytes=[] 33 im_index=0 34 im_byte=0 35 for im_bit in im_snd: 36 im_byte=im_byte<<1 37 im_byte=im_byte|im_bit 38 im_index+=1 39 if(im_index>7): 40 sendbytes.append(int(im_byte)) 41 print(bin(im_byte)) 42 im_byte=0 43 im_index=0 44 45 packetsend(0x12,sendbytes) 46 47 time.sleep(2) 48 packetsend(0x13) 49 50 while 1: 51 print(ser.readline()) 52 ser.close() 53

arduino

1#include <Arduino.h> 2#include <Adafruit_GFX.h> 3#include <Adafruit_SSD1306.h> 4uint8_t incomingByte = 0; // 受信するシリアルデータのために準備 5bool recieving = false; 6uint8_t stats = 0; 7uint8_t data[32]; 8int dataindex = 0; 9uint8_t recievdata = 0; 10uint8_t recievdata2 = 0; 11 12int d_x = 0; 13bool endready = false; 14int d_y = 0; 15#include <SPI.h> 16#include <Wire.h> 17Adafruit_SSD1306 display(128, 64, &Wire, 4); 18void recvdo(uint8_t cval) 19{ 20 21 switch (stats) 22 { 23 case 0x31: 24 data[dataindex] = cval; 25 break; 26 case 0x10: 27 d_x = cval; 28 break; 29 case 0x11: 30 d_y = cval; 31 break; 32 case 0x12: 33 Serial.print(cval, HEX); 34 for (int b = 7; b >= 0; b--) 35 { 36 if (bitRead(cval, b) == true) 37 { 38 display.drawPixel(d_x, d_y, WHITE); 39 } 40 d_x++; 41 if (d_x > 127) 42 { 43 d_x = 0; 44 d_y++; 45 } 46 } 47 break; 48 case 0x13: 49 display.display(); 50 break; 51 case 0x14: 52 display.clearDisplay(); 53 break; 54 55 //display.invertDisplay(); 56 } 57} 58void setup() 59{ 60 Serial.begin(115200, SERIAL_8N1); 61 display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 62 display.display(); 63 delay(1000); 64 display.clearDisplay(); 65} 66 67void loop() 68{ 69 70 // データを受信した場合にのみ,データを送信する 71 while (Serial.available() > 0) 72 { 73 74 // 受信したデータの1バイトを読み取る 75 if (recieving == false) 76 { 77 stats = Serial.read(); 78 recieving = true; 79 memset(data, 0, sizeof(data)); 80 dataindex = 0; 81 } 82 else 83 { 84 85 recievdata = Serial.read(); 86 if (recievdata == 0x29) 87 { 88 endready = true; 89 while (Serial.available() > 0) 90 { 91 recievdata2 = Serial.read(); 92 if (recievdata == 0x29 && recievdata2 == 0x40) 93 { 94 recieving = false; 95 Serial.println("pe"); 96 } 97 else 98 { 99 recvdo(recievdata); 100 recvdo(recievdata2); 101 } 102 } 103 } 104 else if (recievdata != 0x29) 105 { 106 recvdo(recievdata); 107 } 108 } 109 } 110} 111

試したこと

シリアル通信のモード(パリティビットなど)は揃えてみましたが意味はありませんでした

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

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

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

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

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

thkana

2019/11/30 00:36

> 処理してくれません。 観察出来る現象として具体的に何が起こるのですか?
crafter_tarako

2019/11/30 00:41

arduino側でデータを分析する以前にデータを読み込んでくれない(そもそも受信していない?)ということです。
crafter_tarako

2019/11/30 00:43

追記 arduino側でデータの受信を示すRxのledが点灯しませんでした。(データが短いから?)
guest

回答3

0

(追記依頼のコメントを受けて...)

点灯しませんでした

0を含むデータ群が1秒に11500バイト流れ込むなかで(1パケットは小さいでしょうが)、どこが0だかあなたは見分けられるのですか?

調歩同期式シリアル通信(いわゆるふつーのシリアル)ではスタート/ストップビットが付加されますから、データが物理的に1の部分もあり、1byteの0x00の受信でもArduinoのLEDの点灯が視認出来ます。
LEDが全く点灯しないのなら、それはPC(Python)側からデータが出ていないことを意味します。

投稿2019/11/30 04:08

thkana

総合スコア7703

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

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

crafter_tarako

2019/11/30 04:18

回答ありがとうございます。 高スピードレートだと誤差が生じやすいのですね。 ハードウェア関係の知識が乏しいのでそもそもusbの間に流れるデータすら理解できてないので... 例えばですが、流れるデータ(0b00000000)をオン1オフ0でよみとるとすると、 ~000010000000010000~の1の間を読み取っているという認識でいいですか?
thkana

2019/11/30 04:29

なんだか激しく誤解されているような気がしますが... 誤差とかなんの話でしょうか。 あなたが、"LEDが点灯していない"ことを現象として挙げたので、 ・人間の能力でデータ列の中から0x00を弁別することは不可能ではないか(人間は概ね50Hzを超える点滅を認識出来ません) ・0x00でもLEDは点灯する(点灯していないのなら、そもそもデータが送信されていないことになる) という疑問を呈しています。 つまり、観測された事実から、Arduinoでの0x00の処理に問題がある、という推定は導けないのではないかと。 > ~000010000000010000~の1の間を読み取っているという認識でいいですか? まぁ、そう言ってもよいと思います。とりあえず「調歩同期式」でググって...こんなところで参考になりますでしょうか。 https://www.renesas.com/jp/ja/support/technical-resources/engineer-school/mcu-programming-peripherals-03-serial-communication.html
crafter_tarako

2019/11/30 04:36

おっしゃる通り誤解ですね、失礼しました。 解決したんですけど別の問題があって読み取りタイミングがずれることと混同していました。 わざわざ情報まで集めていただきありがとうございます。 無理やりな解決法なんですがPython側で0x0を扱うのはやめておこうかと考えています。 pythonで0x0がどの型に当てはまるのかなど全然わからなかったので... 返事ありがとうございました。
thkana

2019/11/30 04:57

解決したのなら、原因/方法など教えていただけますか。 > Python側で0x0を扱うのはやめておこう Python側の問題であった、ということですか?
ikadzuchi

2019/11/30 05:59

本題ではないですが、1/11520秒間の点灯は余裕で認識できます。 念の為確認してみましたが、 ・87μsは問題なく見える ・10μsは明るい部屋ではやや認識しづらい ・1μsだと手で覆って暗くしないと見えない といったところです。 50Hzを超える点滅は点灯と区別が付かないにせよ認識はできます。 1/50秒より短い点灯の時間を認識できないとして、50/11520=1/230.4ですから、1/230程度の光量の一瞬の点灯として認識することになります。 人間の光に対する感度は3乗か対数か微妙なところみたいですが、とりあえず3乗で計算すると、 (1/230.4)^(1/3)=0.163 から、16%程度の明るさの点灯として認識することでしょう。 計算は適当ですが、実験と大きく(100倍とか)はずれていなそうです。
crafter_tarako

2019/11/30 06:55

>thkanaさん 返事が遅くなりすいません 話がずれてしまいますが、いろいろコードを書いていたところ、大まかな原因はわかりました。 python側でデータを送る間隔が短すぎたようです。 また、arduino側はバッファーフロー?を起こしていたようです。 データのオウム返しをするとなぜか0x0になるので画像のデータがほぼ0x0だったことが原因かと大きく誤解していました。 シリアル通信について、さまざまな情報をありがとうございました。 ikadzuchiさん> わざわざ計算までしてくださりありがとうございます。 意外と一瞬の点灯も目視できるんですね。 LEDの件は点灯しなかった理由はわかりませんでしたが解決しました。(Arduino mega2560の中華コピー品使用のため点灯しなかった?) forループで0x0の送信をしたところ、arduinoのled点灯を確認できました。 コメントありがとうございました。
thkana

2019/11/30 06:59

実験どうも。>ikadzuchiさん 一応私も115200bps設定で0データの1発送信が実機で余裕で見えるのは確認してから回答しています(実際にはTeratermで選択肢があった920Kbpsまで)。この場合スタート/ストップビットでパルスは2発出ていますが。もちろん、これが0なのか'A'なのかなんてわかるわけではありませんが。
thkana

2019/11/30 07:04 編集

後付でいうのもなんですが、それはちょっと気になっていました>バッファオーバーフロー ハンドシェークでもしないとダメかもですね。...って、自己解決の回答でやってるか。 ただ、それはそれとして、質問の時点の一方的通信で「LEDが点灯しない」というモードは起こりうるのかしら?
ikadzuchi

2019/11/30 07:06

ああ、よく考えてみると「1バイト分の点灯が認識できない」ではなく「送信された内容の区別がつかない」という指摘でしたか。すみません、返答がずれていました。
guest

0

どーみても0x00を受信したときはそのデータは捨てられてるようですが。
これをどうしたいんでしょう


んで、送信側のコードですが、致命的な欠陥があります
パケットの開始と終了に0x12と0x13をおいて判別しようとしてますが、これができるのは、
パケットのデータにそのコードが出てこない場合に限られます
パケット中に0x13が出てきたらどうなるのかを考えてみましょう

投稿2019/11/29 15:45

編集2019/11/29 23:24
y_waiwai

総合スコア88038

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

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

crafter_tarako

2019/11/30 00:08

すみません、データが破棄されているのがどこかさっぱり何処か理解できません... データの破棄が一体どこで起きているのか、そもそも0x0は電気的に0b00000000(ずっとオフ)として送信されるのかなどいろいろ不明なままです。 変数を新たに作った時、uint_8で0を代入した時と0x00を代入した時、中身は同じものととらえて大丈夫ですか? また、0x0と0x00は同じものとして考えていいですか? 専門分野外の質問だったらすいません。 ・念のためパケットについて 読みにくいコードですみません。cvalではなくstatsの中身でswitch分岐しています パケット?の中身は、arduino側で見ると一バイト目を処理モードの変数に代入&フラグを有効に、その後nバイトでデータの受信、0x29を受信した時点でデータを一時保留、その段階で0x40を受信したらパケット終了&フラグ無効、それ以外で保留バイトと受信バイトの処理を続行と定めています。 今のところは内容かぶりによる誤作動については検討中です。 ちなみに0x12,0x13の処理モードはこのように割り振っています、コメントアウト部の切り取りです⇩ #0x12 画像バイナリ送信 #0x13 反映 長文失礼しました。
y_waiwai

2019/11/30 00:16

> 0x0と0x00は同じものとして考えていいですか 全く同じです > その後nバイトでデータの受信 そのデータの受信、のデータはどこに入れてますか?
crafter_tarako

2019/11/30 02:10

バイトは一回一回分けて送っています。 受信するときは処理関数recvdoへと値を渡し、 128x64のoledディスプレイにそのまま流しています。 1バイトの情報をを1ビットごと読み込んで、display.drawPixel(x,y,color);でライブラリ内のバッファーに一時保存、 display.display();で反映です。 x,y座標は変数で管理しているので、1バイトのデータででx方向に8ピクセル書き込み、、右端到達でy+=1,x=0としています。
y_waiwai

2019/11/30 02:17

ああ、バイトデータを直接ドットに描画してるのね。 そしたら、 > if (bitRead(cval, b) == true) > { > display.drawPixel(d_x, d_y, WHITE); > } WHITEを描いてるところはあるけど、BLACKを描いてるところはないですよ。 WHITEを上書きしていってるだけで、画面には白しか出てきません ここでたとえ0x00を送ったとしても画面にはなんの変化も起きませんね
y_waiwai

2019/11/30 02:19

で、その画面に描画してるデータで、たまたま0x13というデータがあった場合はどういう動作をするのか考えてみましょう
crafter_tarako

2019/11/30 02:49 編集

black=何も描写しないと同義、最初に0x14,0x0,0x29,0x40を送信することでclearDiaplay()にて画面を黒(光らせない)で埋めています。 0x13を受信してもそこに関しては大丈夫です switch (stats=モード){略}で、実際に処理するのは受信データ、cvalです。 モード変更は0x29,0x40を送信した後、0x1nを送信することでstatsを更新しています。 結局0x29,0x40を偶然受信してしまうことはあるかもなので後々どうにかする予定です。 一応用意した画像に0x29,0x40がないのは確認済みです。 因みに、問題となっているのは、0x0を連続で送った時などにxの座標が変化せず、その後の0xFFなどをおくると描画位置がずれてしまうことです。 0x0について念のためサンプルスケッチ ```arduino int incomingByte = 0; // 受信するシリアルデータのために準備 int kaisuu=1; void setup() { Serial.begin(9600,SERIAL_8N1); // シリアルポートを開き,データレートを9600 bpsにセットする } void loop() { // データを受信した場合にのみ,データを送信する while (Serial.available() > 0) { // 受信したデータの1バイトを読み取る incomingByte = Serial.read(); Serial.print(kaisuu); // 受信したデータを出力する Serial.print("I received: "); Serial.println(incomingByte,BIN); kaisuu++; } } ``` ```python import serial import time with serial.Serial('COM14',baudrate=9600) as ser: >>time.sleep(3) >>for i in range(100): >>>>ser.write(0x0.to_bytes(1,'big')) >>>>print(ser.readline()) ```> インデント にて試しましたが、0x0を繰り返し100回送っても正常に動作しました。
y_waiwai

2019/11/30 02:50

正常に動作するようになったんですね。それはなにより
crafter_tarako

2019/11/30 02:58 編集

えーっと、結局解決はしませんでした。python,arduino両方の仕様がごちゃごちゃしていたのでこの方法はやめました。 その代わりにデータを圧縮する方法として黒が何個、白が何個、黒が何個を繰り返す通信法を採用しようと思います。参考>https://shonen.hateblo.jp/entry/2018/01/30/025802 お付き合いいただきありがとうございました。
guest

0

自己解決

原因がわかりました
原因は、python側でforループで短間隔でデータを送っていたことのようです。

解決法としては、arduino側でserial.printlnで何らかのデータを返し、python側で受け取ったら次のデータを送るという方法にしました。

python

1for: 2 ser.write() 3 ser.readline()#データ受け取るまで待機

因みに、115200bpsを1152000bps,10倍にしても正常に動作しました。
画像一枚転送に0.5秒かかるようになってしまいましたが形にはなりました。

回答してくださった方、ありがとうございました。

#####追記
完成形のコードです

python

1def Ssend(data): 2 bfl = 0 3 for val in data: 4 ser.write(val.to_bytes(1, 'big')) 5 ser.flush()#書き込み完了待機 6 bfl += 1 7 if(bfl > 60): 8 time.sleep(0.005)#オーバーフロー回避 9 bfl = 0

投稿2019/11/30 07:02

編集2019/11/30 07:33
crafter_tarako

総合スコア4

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

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

thkana

2019/11/30 07:09

Arduinoの受信バッファは16バイトだったかな。(64だった気も?) そこまではいっぺんに送っても大丈夫です。1byteずつ同期をとるよりは効率よく出来るはず。
crafter_tarako

2019/11/30 07:11

なるほど。確かに送信>受け取りのタイムロスは大きいですよね。今調べたところser.flush()というものがあったので試してみます!
crafter_tarako

2019/11/30 07:22 編集

def Ssend(data): > bfl=0 > for val in data: >> ser.write(val.to_bytes(1, 'big')) > > ser.flush() > > bfl+=1 >>if(bfl>60): >>>time.sleep(0.005) >>>bfl=0 コードを差し替えたところ0.8sec> 0.05secで処理が終わりました! アドバイスありがとうございました。
thkana

2019/11/30 07:24

>ser.flush() ちゃんと考えてないけど、それは意味が違う気がする...
crafter_tarako

2019/11/30 07:30

ser.flush()は書き込み完了まで待機するコードです。 オーバーフロー回避のため60バイト送信ごとに0.005秒の余白を用意しました。 arduino側の処理は軽いコードなので少し大きめに見積もって0.005秒で十分だと思いまして。
thkana

2019/11/30 07:45

つまり、ハンドシェークでなく送信の間隔を開けただけ、ということですね。 まぁ、それでちゃんと動くならいいですけれど。
crafter_tarako

2019/11/30 07:49

そういうことですね、あまり難しいコード書くと自分でもわからなくなってしまうので。 行き当たりばったりでやるので、エラーが出たら対策はしますが動くならあまりいじりたくないんですよね。
thkana

2019/12/01 03:27

> 行き当たりばったりでやるので、 と > 動くならあまりいじりたくない とは必ずしもつながらないような気はしますが...
crafter_tarako

2019/12/01 05:52

あっ... たぶんその時いろいろ焦っていたので文については勘弁してください()
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問