Visual Basic 中学校 掲示板 投稿内容
タグのない投稿を抽出 統計 RSS

Visual Basic 中学校 > 投稿一覧 >

シリアル通信の受信データを表示 解決済み

タグの編集...

投稿者 vb素人   (学生)   投稿日時 2017/6/21 09:49:15
Serial Portで受信したデータを表示させる方法について。

次のコードでSerial Portで受信したデータを表示させるプログラムに取り組んでいますが、
全くデータが表示されなくて困っています。

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        'Tickイベント (Timerプロパティで指定された間隔の時間が経過したときに発生するイベント)
        AddHandler Timer1.Tick, AddressOf Data_Check
    End Sub

    Private Sub Data_Check()
        If (SerialPort1.BytesToRead > 0) Then
            '入力バッファ内のラインフィード(行末)まで読み取ったデータをrdatで宣言

            Dim rdat As Integer = SerialPort1.ReadLine()
            TextBox2.Text = rdat


        End If
    End Sub


[今の状況]
送信側からは、1秒おきに"Q" (ASCIIコード 51)を送信しています。
(正しく送信できていることは、信号波形とXCTU(XBee設定ツール)で確認しました。)

投稿者 daive   (社会人)   投稿日時 2017/6/21 11:59:18
今回の様な方むけのサイト
NonSoft さんのサイト:通信関係などで困った時は、参考に。
http://nonsoft.la.coocan.jp/

>SerialPort1.ReadLine()

SerialPort.ReadLine メソッド ()
https://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport.readline(v=vs.110).aspx?f=255&MSPPError=-2147217396&cs-save-lang=1&cs-lang=vb#code-snippet-1
⇒msdn が見れない方は、microsoft アカウントを取得してmsdnへログインしてください。
 msdn / technet など種々公開情報があります、MSの事は、MSのサイトが一次情報となります。
 時間が取れる方は、1日1時間程度、MSDN / TechNet を渉猟してください。

トラブルを避けるためには、
シリアル通信では、一旦Byte配列で受信したり、
通信データが、ACIIのA-Z / a-z など文字のみと
判明している場合のみ、文字列で受けたり。


投稿者 daive   (社会人)   投稿日時 2017/6/21 12:39:02
質問をする際は、
 ハードウェアがらみの場合は、
 使用している機材と型番(今回は、XBEEを使っているようですが、?)は、
 重要な情報です。
 マイコンピュータ⇒管理⇒デバイスマネージャの、
 COMポート情報では、COMポートとして認識されているかどうかや、
 COMポートの設定、確認できます。
 古いWindowsの場合は、COM10以上で制約があったり。

今回は、送信側は、ツールで送信出来ているのが確認できているらしいですが、
受信側は、何を使って、どの様に受信しているかが書かれていません。
ハードウェアの全体構成が不明ですよね。

単純に送信/受信チェックをするだけであれば、
Tera Termなどの、ターミナルソフトで
確認できます。
⇒動作しないアプリで悩む必要が無いし、此処まで確実って、1個づつ検証しないと、
  徒手空拳では、先が見えなくて、何が悪いか解らなくなってしまいます。

ヒント
 ReadByte メソッド
 ReadChar メソッド
 ReadExisting メソッド
 ReadLine メソッド
 ReadTo メソッド (String)
 Write メソッド
 WriteLine メソッド (String)
まだまだ、SerialPort クラス には、色々ありますが、
しっかり調べて、適切なメソッド、プロパティを使う必要があります。
⇒知らなければ使えないですよね?そのために、msdn / TechNetを渉猟します。
  渉猟して、知識の引出を増やすことが必要です。

通信の準備
1.通信速度等を合わせる
  スタートビット:1、1.5、2
      PC向けでは無いが、マイコンなどでチップをプログラム出来る場合は要考慮
  ストップビット:1、1.5、2
  ボーレート: テレメなどは、50bpsが現役
  パリティビット: 偶数:EVEN、奇数:ODD、パリティ無:NON
  ⇒データとして扱われるが、プロトコル有りの場合は、プロトコルサポート
2.全2重、半2重通信の確認、動作条件の確認
  半2重では、DCDライン(CD)、または、着呼での動作が規定されている場合がある。
  RTS/CTS、DTR/DSR のハンドシェイク有り無し、XON/XOFF有り無し
3.PC側、
  シリアルチップの確認:RTS/CTS、DTR/DSR、DCDの処置が必要かどうか
  USBシリアル:デバイスドライバ経由での、ハンドシェーククラインの動作確認
  XBEEなど:デバイスドライバ経由での、ハンドシェークラインの動作確認




投稿者 vb素人   (社会人)   投稿日時 2017/6/21 13:03:18
みなさま

私の説明が悪くてすみません。
環境について説明します。

・送信側
  マイコン+Xbee
・受信側 Xbee+PC

続いて今の状況ですが、
PCの所で、TeraTermを使用して送信側から送られてきているデータ('Q')を確認することができました。
まずは、この('Q')をvbで表示できるようにしたいです。

[今後行いたいこと]
送信側から10bitのデータをj送信し、受信側で1秒おきにデータを受信し、グラフ化(縦軸が10bitデータ、横軸は時間)したいと考えています。

単にデータだけの確認であれば、TeraTermから確認することができたのですが、
リアルタイムに受信データをグラフ化するところまで実現したいです。

今は、↓のプログラムで試していますが、
受信データを表示するTextBoxに何も表示されずに困っています。

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        'Tickイベント (Timerプロパティで指定された間隔の時間が経過したときに発生するイベント)
        AddHandler Timer1.Tick, AddressOf Data_Check
    End Sub

    Private Sub Data_Check()
        If (SerialPort1.BytesToRead > 0) Then
            '入力バッファ内のラインフィード(行末)まで読み取ったデータをrdatで宣言

            Dim rdat As Integer = SerialPort1.ReadLine()
            TextBox2.Text = rdat


        End If
    End Sub


投稿者 daive   (社会人)   投稿日時 2017/6/21 15:33:44
>Dim rdat As Integer = SerialPort1.ReadLine()
>             TextBox2.Text = rdat

ブレークポイントを設定して、
何が起きているか、
SerialPort1.ReadLine()
データを読み出すのは、↑で良いかから、確認してください。
⇒プログラムは、コードを書いただけでは、動きません、必ずデバッグが必要です。
  デバッグ機能を十二分に使えて、動くコードが書けると思ってください。

ReadLine に付いて調べましたか?
今回の場合は、
SerialPortで、1文字または、複数文字を、ターミネーション無で、
受信したいのですから
設定したターミネーション(行末コード)があるまで、受信しつづける
では、具合が悪いですよね?
SerialPortクラスのメソッドを調べて、自身で使いたい、実現したい内容の、
メソッドを使う必要があります。

ヒントに書きましたが、
SerialPort クラス 
には、なぜ?これほど沢山のメソッド、プロパティがあるのでしょうか?
考えてみてください。

投稿者 vb素人   (社会人)   投稿日時 2017/6/22 15:46:58
daive さま

ヒントを教えていただき、ありがとうございました。
Dim data As String
data = SerialPort1.ReadExisting()

今回は文字データの受信でしたので、ReadExisting()メソッドを使用しました。
その結果、送信側から送られてくる文字を受信していることが確認できました。

次のステップとして、送信側から10bitのデータを送信し、
それを同じく受信側(vb)で表示させたいと思います。

送信側のデータを1023とした場合、
当たり前ですが、ReadExisting()メソッドで受信した場合は、('?')が表示されてしまいます。
ReadByte()メソッドで受信した場合は、(255)と表示されます。

10bitのデータを受信するためにどのメソッドを使用すれば良いのか分かりません。
教えていただけないでしょうか。

投稿者 daive   (社会人)   投稿日時 2017/6/23 11:52:41
一歩前進の様に見えて、かなりな前進かもです。
調べる癖が付くと、掲示板で聞いて、答えが返って来るまで
~~~~~待ち~~~~
という、時間が節約できます。

>・送信側
>  マイコン+Xbee
>・受信側 Xbee+PC
この構成で、
送信側データ,、コマンドレスポンスにするのか、垂れ流しにするのかを決めます。
それを決めたら、送信するデータは、
1.文字としての数値なのか、バイナリー値、BCD値なのか
  0 :16進数で、0x30 なのか、 0x00 なのかという話
  1023: 0x03FF なのか、0x1024 なのか?バイナリー、BCDの別
2.文字でも、バイナリー値でも、固定長なのかどうか?
  0~1023 を
  文字表現の数値であれば、
  0、1 、、、、 999、1000
  なのか
  0000、0001、、、、 0999、1000
  とするのか
3.送信データを、ターミネーションするのかどうか
  簡易的に、文字表現の数値を送る場合
  DT0000 C/R L/F
   :
  DT1023 C/R L/F
'
 DT:コマンド、レスポンス文字
 文字表記の数値4桁:計測値
 C/R : 0x0D
 L/F : 0X0A
4.コマンド、レスポンスを決める必要があるかどうか。
 種々設定を、PCから行いたい。データに日時を入れたい。通信速度設定をしたい。など
 A/D値:0~1024 の値でなく、レンジ値0~1024:0.00~5.00Vとしたいなど

手書きで良いので、
 種々模式図
 状態遷移図
 通信用フロー図
 真理値表(これが書けると、ロジックが簡単になる場合がある)
などが書ける様になると、他に説明が出来る様になります。

なぜ、ターミネーションの話が出て来たか?
データをターミネーション無で、送った場合何が起きるでしょうか?
特に垂れ流しの場合は、どうなるでしょうか?
おおざっぱに、通信では、厳密に通信フレームを構築する方法と、
電信から続いている、文字での方法とが、あります。
人間が認識する場合は、文字として読めれば、同音異義語の注意は必要にしろ
結構なんとかなります。
プログラムでは、どうでしょう?
⇒コンピュータは融通が効きません。プログラムは書かれた様に動きます。

さらなる難関。
イベント駆動型プログラミングでは、
無限ループ、や、時間待ち、データ待ちの、ループは禁忌です。
⇒じゃあどうするか?
  イベントを取得して、都度、処理していきます。
  その時の状態、状況を考えるのに、状態遷移図が必要になります。
  安直には、FIFOをコンボボックス、リストボックスで実装して、見える化して
  確認します。
  デバッグ機能を本格的に使う必要が出てきます。




投稿者 daive   (社会人)   投稿日時 2017/6/23 11:59:20
×:1023: 0x03FF なのか、0x1024 なのか?バイナリー、BCDの別
○:1023: 0x03FF なのか、0x1023 なのか?バイナリー、BCDの別

ついでに、エンディアン(バイトオーダ )
https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3


投稿者 daive   (社会人)   投稿日時 2017/6/23 12:39:04
お茶タイム:ふんふんと、読み流してください。

無線通信では、常に理想的な電波状況にあるとはかぎりません。
そこで、
設置時には、
マイコン側から、垂れ流しでデータが送られ来るモード。
PC側から送った文字が、エコーバックしてくるモード。
などがあると、役に立つ場合があります。
(何れも、ターミナルソフトで簡易確認が出来る様にしておきます。)

実際に送受信するデータは、
8ビット、EVEN/ODDどちらでも良いので、パリティを付ける。
⇒当然、パリティエラーのイベントを拾って、データを捨てる処理が必要です。
データ全体に対して、チェックサムで良いので、データの正当性を判別する物を付けます。
(マイコンでCRC32を計算させるのは荷が重いかも)
1回の送受信のデータの長さが、256バイトを超える様になったら、CRCの導入を考えます。

通信プログラムの肝は、エラー処理、エラーリカバリにあります。
通信エラーを前提としない、無線通信プログラムは、
理想的な電波環境が保たれない場合に、容易に破綻します。
(起きた事象のログを残す様にすると、後々役立ちますが、
 そこまで、手が廻るようになるのは、もう少し時間がかかります。)


投稿者 vb素人   (社会人)   投稿日時 2017/7/4 07:58:48
10bitのデータを受信できずに行き詰っています。

送信側からは"1023"を送っています。

当たり前ですが、ReadExisting()メソッドで受信した場合は、('?')が表示されてしまいます。
ReadByte()メソッドで受信した場合は、(255)と表示されます。

私の調査不足であることは十分認識していますが、
10bitのデータを受信することはできないのでしょうか。

どなたか教えていただけると助かります。

投稿者 daive   (社会人)   投稿日時 2017/7/4 12:10:54
>10bitのデータを受信することはできないのでしょうか。
やり方がわかれば、なーんだこんな事だったの?程度の事なのですが。。。
まずは、仕様を書いて、まとめましょう。
必要な情報を集めましょう。
今回は、2つの要因があるので、簡単な方から、正当性の確認をしましょう。

シリアル通信は、古い手順だけに設定項目が違っていると送受信できませんし、
データ自体が、文字なのか、バイナリーなのか、かつ、固定長なのか、可変長なのか
きちんと定義をして、プログラムをそれなりに考えないと、うまくいきません。

SerialPort クラス
https://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport(v=vs.110).aspx

SerialPort.ReadByte メソッド ()
https://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport.readbyte%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

<NonSoftさんのサイト>
通信関係のサンプルが多く掲載されています。
但し、あくまでサンプルです。

通信ツール/通信サンプルソース
http://nonsoft.la.coocan.jp/top5.html

COMポート(シリアル)での送受信サンプル(C#/VB.NET)
http://nonsoft.la.coocan.jp/SoftSample/CS.NET/SampleRs232c.html



投稿者 vb素人   (社会人)   投稿日時 2017/7/4 13:07:56
>やり方がわかれば、なーんだこんな事だったの?程度の事なのですが。。。

そう言わてしまうと何も言えません。

答えを安易に求めている姿勢が不快に思われるかもしれませんが、
色々調べても分からず困って、この掲示板で答えを求めています。

答えを教えていただけると助かります。


投稿者 daive   (社会人)   投稿日時 2017/7/4 13:43:46
全く解らない状態の御様子なので、

COMポート(シリアル)での送受信 サンプル(C#/VB.NET)
http://nonsoft.la.coocan.jp/SoftSample/CS.NET/SampleRs232c.html
より、
Byte 受信:1文字以上

’受信文字数:既にPC側にて受信バッファ内に受信しているデータ数を示します。
’↓ここで、受信文字数分のByte配列を用意している、-1なのはベーシック系の配列定義の御作法
Dim dat As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}
’↓ここで、先に指定した受信文字数分のデータを、受信バッファより取り出す
        SerialPort1.Read(dat, 0, dat.GetLength(0))

’この後、文字データなのか、バイナリーなのか、BCDなのかで、適したエンコード処理を行う。
’↓ここでは、メッセージボックスへ、シフトJISエンコードしたデータを表示している。
MessageBox.Show( _
            System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(dat))



Byte で受信した値は、配列に、0x00~0xFF の範囲の値が入るので、
送受信されるデータが、
ASCII、SHIFT-JIS など文字なのか?
バイナリーなのか?BCDなのか?
が、不明では、エンコード出来ない事になります。

0x00~0xFF:0~255(10進数)

もし、1023をバイナリーで送受信しているのであれば、
1023:0x03FF なので、1バイトで表現できない。
エンディアンが、LowByte HighByte の順であれば、
送信されるデータは、 
0xFF
0x03
と、2バイトで構成されている筈。
ところが、これでは一旦こけると、データズレが起きて、役立たずになる。
⇒バイナリーでの送受信では、通信フレームが必須。

取りあえず、此処まで。


投稿者 daive   (社会人)   投稿日時 2017/7/4 14:09:53
関連検索
検索ワード:VB.net byte 配列 数値
https://www.google.co.jp/search?hl=ja&q=VB.net+byte+%E9%85%8D%E5%88%97%E3%80%80%E6%95%B0%E5%80%A4&lr=lang_ja&gws_rd=ssl

byte から数値他の変換の方法を探す、検索の足掛かりにしてください。

実用品を作るには、
VB.NET で、Windowsフォームプロジェクトで、
ツールボックスから、serialPort を貼り付けた場合に
DataRecived 
ErrorRecived
PinChanged
イベントがある筈です。
これらを、各々適切にプログラムする事が必要になります。

今回は、Xbee を使う様子ですが、USB-COMアダプタでは、
仮想COMドライバの出来により、Windows XP / 7 / 8.1 / 10 x32,x64で
ステータスラインの変化イベント、データエラーでのイベントでの
挙動が違う場合があるのを確認しています。


投稿者 kiku   (社会人)   投稿日時 2017/7/5 09:14:37

MessageBox.Show(System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(dat))



MessageBox.Show(BitConverter.ToString(dat))

へ変えて頂ければ、どんな受信データでもエラーにならずに
結果を得られます。
その結果を掲示板にご連絡頂ければ
どんなルールで送信されているか推定できるかと思います。

投稿者 vb素人   (社会人)   投稿日時 2017/7/5 12:14:14
理解が悪くて本当にすみません。

試してみたことを整理します。
①SerialPort1.ReadByte()で読み取ると、"255"が表示される
②SerialPort1.ReadExisting()で読み取ると、"?"が表示される(ASCIIコード表で0x3FFに該当する文字)

③続いて、複数バイト読み取るため、受信データをbyte配列で宣言して試しました。
            'Dim data As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}
            'SerialPort1.Read(data, 0, data.GetLength(0))

            'System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(data)

これで試した所、次のメッセージが出てきてしまいます。

型'System.Byte[]'のオブジェクトを型'System.String'に変換できません


試しているコードは次の通りです。(①を試したときのコード)


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try
            Dim data As String
            data = SerialPort1.ReadByte()

            Dim args(0) As Object
            args(0) = data
            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), args)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub


    Private Sub RcvDataToTextBox(data As String)

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            RcvTextBox.AppendText(data)
        End If

    End Sub

投稿者 kiku   (社会人)   投稿日時 2017/7/5 14:02:31

>②SerialPort1.ReadExisting()で読み取ると、
>"?"が表示される
>(ASCIIコード表で0x3FFに該当する文字)

・送信情報は、"1023"である。

・送信情報を2進数文字列にする。
 1.文字列から数値にする。"1023" → 1023
 2.数値のint型内部表現に変換。1023 → 0x000003FF
 3.int型内部表現を2進数に変換 0x000003FF → 00001111111111
 4.10bit分の1があることがわかる。

・一方、②での受信文字列は、0x3FFである。

・送信情報と一致していることがわかる。

投稿者 (削除されました)   ()   投稿日時 2017/7/5 14:19:03
(削除されました)

投稿者 vb素人   (社会人)   投稿日時 2017/7/5 14:59:51
kiku様

回答ありがとうございます。
教えていただきました通り、
MessageBox.Show(BitConverter.ToString(dat)) 
に変更してみましたところ、

メッセージBoxには、「FF-FF-FF」が表示されます。(FF-FFが連続して表示)
⇒送信しているデータは、1023なので、「FF-03」または「03-FF」と表示されると考えていましたが、
なぜか「FF-FF」となってしまいます。

その後、次のメッセージが表示されます。
「型'System.Byte[]'のオブジェクトを型'System.String'に変換できません」


テストした際のコードを掲載させていただきます。


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try
             Dim data As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}
            SerialPort1.Read(data, 0, data.GetLength(0))

            MessageBox.Show(BitConverter.ToString(data))

            '受信したデータをテキストボックスに書き込む.
            Dim args(0) As Object
            args(0) = data
            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), args)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub


    Private Sub RcvDataToTextBox(data As String)

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            RcvTextBox.AppendText(data)
        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/5 15:22:43
> 試してみたことを整理します。

本来は③を使いたかったけれども、うまくいかなかったので
今回の要件には合わないと理解したうえで、とりあえず
①や②を試行してみた状況とお見受けします。


> 型'System.Byte[]'のオブジェクトを型'System.String'に変換できません

このエラーは、どの行で発生していますか?

それと、DataReceived イベントを使う場合は、e.EventType の値にも気を配った方が良さそうです。
http://d.hatena.ne.jp/defint/20090729/1248877026


> ③続いて、複数バイト読み取るため、受信データをbyte配列で宣言して試しました。
> 'Dim data As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}
> 'SerialPort1.Read(data, 0, data.GetLength(0))
> 'System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(data)
3 行目は、
 Dim strData As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(data)
ではありませんか? 折角変換しても、その結果を受け取らないと意味がありません。

ちなみに 1 行目は、
「Dim data As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}」は
「Dim data(SerialPort1.BytesToRead - 1) As Byte」
とも書けます。


ついでに言えば、シフトJIS を指定する場合には、
 .GetEncoding("shift_jis")
 .GetEncoding(932)
のいずれかを個人的にはお奨めしています。

"SHIFT-JIS" 指定も間違いでは無いのですが、これは別名表記だからです。
(他の別名として: "csWindows31J"、"csShiftJIS"、"x-sjis"、"x-ms-cp932"、"ms_Kanji")



> 試しているコードは次の通りです。(①を試したときのコード)
これについては、データ型の指定に問題がありますので、
『Option Strict On』を有効にしておくことをお奨めします。


また、Delegate_RcvDataToTextBox の定義も漏れているようです。
とはいえ、わざわざ自前でデリゲートを用意せずとも、
 Invoke(New Action(Of String)(AddressOf RcvDataToTextBox), data)
として利用可能な、Action(Of ) デリゲートを使うことが可能です。


> ①SerialPort1.ReadByte()で読み取ると、"255"が表示される
> Dim data As String
> data = SerialPort1.ReadByte()
Dim data As String ではなく
Dim data As Integer で受け取るべきです。

それはさておき、data が 255 になってしまうという状況についてですが、
このメソッドから、1023 という 10bit の値が直接返されることはありません。


ReadByte のデータ型は、32 bit 長である「Int32 型」で定義されていますが、
実際に返却される値は、8 bit 長である「Byte 型」のデータであるためです。

ちなみに、なぜ ReadByte が As Byte ではなく As Integer になっているかというと、
ストリームの末尾が読み取られた場合に限り、&H00~&HFF の範囲ではなく、
-1 を返すように設計されているためです。


ReadByte を 2 回呼べば、&H3FF という 10bit 値を
2 つの 8bitデータ( &HFF と &H3 )で受け取れるのでは無いでしょうか。
もしくは、③ 案の Read メソッドを使うかですね。



> ②SerialPort1.ReadExisting()で読み取ると、
今回受け取りたいデータは「文字」ではないので、
どちらにせよ、このメソッドの出番は無いと思います。

仮に文字を読み取る必要があったとしても、ReadExisting は、
マルチバイト系のエンコーディング(Shift_JIS 等)の先導バイト判定を
必ずしも考慮しないため、データの区切り位置によっては
正しい文字として判定されない可能性があります。ご注意ください。

【区切り位置で化ける文字の例】
0x8181 "="
0x8182 "≠"
0x8281 "a"
0x8282 "b"



> "?"が表示される(ASCIIコード表で0x3FFに該当する文字)?
"?" の ASCII コードは 0x3FF ではなく 0x3F ですね。

そもそも ASCII コード表 に 0x3FF は定義されていません。
本来の ASCII とは、7bitコードのことであり、0x00~0x7F のみが定義されています。
(エンコーディングとしては System.Text.Encoding.ASCII が該当します)


本当に 0x3FF なのだとしたら、『ASCIIコード表』というのが
何かの間違いなのだと思います。

たとえば、シフトJIS のキャラクタコード表が、
拡大解釈されて ASCII コード表と紹介されていることがありますが、
その場合でも 0x03FF や 0xFF03 はありえないでしょう。

0x03 は ␃ を意味する制御文字ですし、
0xFF に至っては未定義領域ですから。


ちなみに Unicode だと、
03,FF (U+FF03) には "#" (FULLWIDTH NUMBER SIGN)
FF,03 (U+03FF) には "Ͽ" (GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL)
が割り当てられています。

Dim FullWidthNumberSign As String = "#"
TextBox1.Text = AscW(FullWidthNumberSign).ToString("X4")
TextBox2.Text = System.BitConverter.ToString(System.Text.Encoding.Unicode.GetBytes(FullWidthNumberSign))


投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/5 15:37:05
> その後、次のメッセージが表示されます。
Option Strict On を宣言しておけば、実行時エラーではなく、
コンパイル時点で問題点に気が付くことできます。


> 「型'System.Byte[]'のオブジェクトを型'System.String'に変換できません」

③案で上記のエラーが出るのは、バイナリーを文字列として渡そうとしているためです。
要するに、「データ型が間違っているから」ということです。


> Dim data As Byte() = New Byte(SerialPort1.BytesToRead - 1) {}
変数 data は「Byte 型の一次元配列」として宣言されていますよね。
これが 型'System.Byte[]' に相当します。


> Private Sub RcvDataToTextBox(data As String)
最終的に受け取るのは上記のメソッドですが、この引数は「String 型」ですよね。
これが 型'System.String' に相当します。


この違いがあるため、エラーが発生することになります。
バイナリーを文字列型引数に渡そうとするのではなく、
どちらかの型に統一するようにしてみてください。

【A案】バイナリーに統一する場合:
「Private Sub RcvDataToTextBox(data As Byte())」に変更してみてください。

【B案】文字列に統一する場合:
Invoke に渡す引数 arg を、「String 型の変数」に変更してみてください。

投稿者 vb素人   (社会人)   投稿日時 2017/7/5 17:18:32
魔界の仮面弁士さま

回答ありがとうございます。

ご指摘の通り、本来は③を使いたいのですが、
①、②を試していた所です。

③(Readメソッド)を使う方法で10bitデータを受信するため、
次のコードでテストしてみました。

ご指摘がありました通り、Option Strict Onの状態で確認しています。
MessageBoxでdataの値を表示させると、「FF」と表示されますが、

その後、次のエラーメッセージが出てきます。
「型'System.Byte[]'のオブジェクトを型'System.String'に変換できません」

このメッセージが出ている所は、
args(0) = data
です。



    Private Delegate Sub Delegate_RcvDataToTextBox(data As Byte)

    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        'シリアルポートをオープンしていない場合、処理を行わない.
        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try
            Dim data(SerialPort1.BytesToRead - 1) As Byte
            SerialPort1.Read(data, 0, data.GetLength(0))


            MessageBox.Show(BitConverter.ToString(data))
            'Dim strData As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(data)
            'MessageBox.Show(strData)

            '受信したデータをテキストボックスに書き込む.
            Dim args(0) As Object
            args(0) = data
            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), args)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub RcvDataToTextBox(data As Byte)

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            RcvTextBox.AppendText(data)
        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/5 17:35:41
> SerialPort1.Read(data, 0, data.GetLength(0))
この場合は
 SerialPort1.Read(data, 0, data.Length)
の方がスマートかと思います。


私の指摘は
> 【A案】バイナリーに統一する場合:
> 「Private Sub RcvDataToTextBox(data As Byte())」に変更してみてください。
でしたが、修正されたコードが
> Private Sub RcvDataToTextBox(data As Byte)
になっていますね。配列で受けてください。


> RcvTextBox.AppendText(data)
AppendText メソッドに渡すべき引数は String 型です。
この場合の data は String では無いので、Option Strict On ではエラーになるでしょう。

data が Byte 型なのだとしたら、
 RcvTextBox.AppendText(CStr(data))
 RcvTextBox.AppendText(data.ToString())
 RcvTextBox.AppendText(Hex(data))
 RcvTextBox.AppendText(data.ToString("X"))
などを試してみてください。

「data As Byte」を、「data() As Byte」もしくは「data As Byte()」に修正した場合は、
 RcvTextBox.AppendText(BitConverter.ToString(data))
などを試してみてください。



もう一つ。

Invoke メソッドの引数は ParamArray になっていますので、
わざわざ Object 配列に詰めなおす必要は無いと思います。
具体的には
Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)
で良いと思いますし、仮に Object 配列に詰めるとしても、変数を用意せずとも
Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), New Object() { data })
のように書けるかと思います。

投稿者 vb素人   (社会人)   投稿日時 2017/7/5 18:13:05
魔界の仮面弁士さま

何回も申し訳ありません。
少し進んだような気がします。

↓のコードで確認をした所、
受信データは、「FFFFFFF」と確認できました。(FFがずっと続く)

しかし、送信側からは1023を送信してきているため、
受信データは、「03FF」が表示されると考えています。

なぜ、FFと表示されてしまうのか理解できておりません。
教えていただけると助かります。



    Private Delegate Sub Delegate_RcvDataToTextBox(data As Byte())

    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        'シリアルポートをオープンしていない場合、処理を行わない.
        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try

            Dim data(SerialPort1.BytesToRead - 1) As Byte
            SerialPort1.Read(data, 0, data.Length)

            Dim args(0) As Object
            args(0) = data
            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub RcvDataToTextBox(data As Byte())

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            'RcvTextBox.AppendText(CStr(data))
            RcvTextBox.AppendText(BitConverter.ToString(data))
        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/5 20:14:41
> (FFがずっと続く)
1023 を送信していないときに、FF が送出され続けているということでしょうか?
それとも、1023 を一回送信したときに、FF の羅列が止まらなくなるということでしょうか?


> Dim args(0) As Object
> args(0) = data
この変数は、もはや使われていないようです。

それと、SerialPort クラスのプロパティ設定は問題ないでしょうか?
(たとえば、BaudRate、DataBits、Parity、StopBits、Handshake など)


> 受信データは、「FFFFFFF」と確認できました。(FFがずっと続く)
奇数桁の16進数になるのは不自然ですね。

たとえば、Integer 型の『-1』は、&HFFFFFFFF ですが、
FFFFFFF ではなく
FFFFFFFF だったりはしませんか?
Dim bin() As Byte = BitConverter.GetBytes(-1)



Hex 関数を使っていたのであれば、奇数桁になることもありえますが、
今回は BitConverter を使っているはずで、その場合、表記も
"FF-FF-FF-FF" 形式になるはずなのですが…。

現状のコードでいえば、AppendText が複数回 呼ばれた時に、
文字列が繋がって "FF-FF-FF-FFFF-FF-FF-FF" のように、
ハイフンが漏れることはあるかも知れませんが。


ReadByte が -1 を返すケースについては既に述べていますが、
今回は Read をお使いなのですよね。念のため、データ長も
調査するようにし、データの確認も TextBox ではなく、
Debug を使うようにしてみては如何でしょう。


Dim length As Integer = SerialPort1.BytesToRead
If length = 0 Then
    Return
End If

Dim data(length - 1) As Byte
If SerialPort1.Read(data, 0, data.Length) > 0 Then
    Debug.WriteLine("{0:yyyy/MM/dd HH:mm:ss.fff} {1} len={2} : ", Now, e.EventType, length, BitConverter.ToString(data))
    'Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data) 
End If



投稿者 vb素人   (社会人)   投稿日時 2017/7/6 08:06:51
魔界の仮面弁士さま

私の説明が悪くて申し訳ありません。

①送信側
 1023を0.5秒おきに送信しています。

②受信側(今の状況)
 (受信データを表示するTextBox)に0.5秒おきに「FF」が表示されます。


「1023」を送信しているので、「03FF」が受信されると思っているのですが、
0.5秒おきに「FF」が表示されてしまっています。
これは、2Byteしか受信できていないということなのでしょうか。


③教えていただいたコードでDebugを使用してみました。

その結果は、次の通りとなりました。

2017/07/06 08:03:33.272 Chars len=1 : 
2017/07/06 08:03:33.896 Chars len=1 : 
2017/07/06 08:03:34.504 Chars len=1 : 

すみません。こちらの結果に対する理解が追い付いていません。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/6 10:52:48
> (受信データを表示するTextBox)に0.5秒おきに「FF」が表示されます。
念のために確認。受信時にメッセージボックスを使ったりはしていないでしょうか。

メッセージボックスは、ボタンを押すまで応答が止まる「同期処理」のため、
その間に通信が行われると、データを取りこぼす可能性が出てきます。
(Debug クラスによる確認に切り替えたのもそのためです)


> Dim args(0) As Object
> args(0) = data
この 2 行は使われていないので、削除したほうが良いかと思います。


> 0.5秒おきに「FF」が表示されてしまっています。
> これは、2Byteしか受信できていないということなのでしょうか。
2 桁の FF は 1 バイト(8 ビット)ですよね。&HFF = &B1111_1111 = 255
1 桁の F  なら 0.5 バイト(4 ビット)です。&HF  = &B0000_1111 =  15


データが 2 バイトの固定長なのであれば、
ReceivedBytesThreshold は 1 のままにしておき、
DataReceived イベント内にて、
「2 バイト溜まるまでは読み取らない」とか。

If SerialPort1.BytesToRead < 2 Then
    '受信データが溜まってない場合は、Read せずにスルー 
    Return
End If


投稿者 vb素人   (社会人)   投稿日時 2017/7/6 11:14:05
魔界の仮面弁士さま

度々申し訳ありません。
原因が1つ分かりました。

送信側のプログラムにミスがありました。
大変申し訳ありません。

送信側で1023を送信しているつもりでしたが、
送信側のコマンドが8bit送信になっていたため、
1023のデータを書き込んでも、8bitで送信されるため、「FF」が続く結果となっていました。

送信側を10bit送信にしたうえでテストを行ったところ、

「03 FF」と受信できるようになりました。

ただし、たまに「03-FF」というように-が付く場合があります。
これは何故でしょうか。

また、「03FF」を10進数の数値(1023)に変換する方法はありますでしょうか。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/6 12:10:14
> たまに「03-FF」というように-が付く場合があります。
> これは何故でしょうか。

こういうことです。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim b1() As Byte = {&H1}
    Dim b2() As Byte = {&H23, &H45}
    Dim b3() As Byte = {&H67, &H89, &HAB}

    TextBox1.Text = BitConverter.ToString(b1)
    TextBox2.Text = BitConverter.ToString(b2)
    TextBox3.Text = BitConverter.ToString(b3)
End Sub



> また、「03FF」を10進数の数値(1023)に変換する方法はありますでしょうか。 
元データは Byte 配列として受け取っていますよね。
それを BitConverter クラスで変換してみては如何でしょう。
Dim bin As Byte() = {&HFF, &H3}
Dim a As Short = BitConverter.ToInt16(bin, 0)   '符号付き 2 バイト整数に変換 
Dim b As String = Convert.ToString(a, 10)  '10進数表記の "1023" に変換 


あるいは、『&H3 * 256 + &HFF』や『(CShort(&H3) << 8) + CShort(&HFF)』で算出するとか。

投稿者 vb素人   (社会人)   投稿日時 2017/7/6 17:32:18
魔界の仮面弁士さま

受信データ(03FF)を10進数の数値(1023)に変換する方法について。

↓のコードでテストしていますが、
Dim a As Short = BitConverter.ToInt16(data, 0)   '符号付き 2 バイト整数に変換 
この行で、次のメッセージが出てしまいます。

**ターゲット配列は、コレクションの項目すべてをコピーするには長さが不足しています。配列のインデックスと長さを確認してください。**


data配列の扱いに問題があるのでしょうか。



    Private Delegate Sub Delegate_RcvDataToTextBox(data As Byte())

    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try
            Dim data(SerialPort1.BytesToRead - 1) As Byte
            SerialPort1.Read(data, 0, data.Length)

            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try





    End Sub

    Private Sub RcvDataToTextBox(data As Byte())

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            'RcvTextBox.AppendText(CStr(data))

            Dim a As Short = BitConverter.ToInt16(data, 0)   '符号付き 2 バイト整数に変換 
            Dim b As String = Convert.ToString(a, 10)  '10進数表記の "1023" に変換 
            RcvTextBox.AppendText(b)
 
        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/6 19:47:51
> Dim a As Short = BitConverter.ToInt16(data, 0)   '符号付き 2 バイト整数に変換 

data.Length が 1 の時に処理しようとしていませんか?
変換するのは、データが揃ってからです。

ToInt16 に渡すのであれば、data.Length は 2 以上でなければなりません。

Dim bin3 As Byte() = {&H12, &H34, &H56, &H78}
Dim bin2 As Byte() = {&H65, &H43}
Dim bin1 As Byte() = {&H78}

Dim i5 As Short = BitConverter.ToInt16(bin3, 0) '13330 (&H3412) になる 
Dim i4 As Short = BitConverter.ToInt16(bin3, 1) '22068 (&H5634) になる 
Dim i3 As Short = BitConverter.ToInt16(bin3, 2) '30806 (&H7856) になる 
Dim i2 As Short = BitConverter.ToInt16(bin2, 0) '17253 (&H4365) になる 
Dim i1 As Short = BitConverter.ToInt16(bin1, 0) '長さが足りないのでエラー 
Dim i0 As Short = BitConverter.ToInt16(bin3, 3) '長さが足りないのでエラー 


投稿者 vb素人   (社会人)   投稿日時 2017/7/7 09:06:31
魔界の仮面弁士さま

いつも丁寧な説明ありがとうございます。
data.lengthを2に設定し、2バイト溜まっていなければスルーする形でテストしました。

 Dim data(SerialPort1.BytesToRead - 1) As Byte
 SerialPort1.Read(data, 0, 2)

 If SerialPort1.BytesToRead < 2 Then
 '受信データが溜まってない場合は、Read せずにスルー 
 Return
 End If

しかし、次のメッセージが出てしまいます。

**配列のオフセット及び長さが範囲を超えているか、カウンターがソースコレクションの
インデックスから最後までの要素の数より大きい値です。**

SerialPort1.Read(data, 0, 2)
⇒オフセット0で、2バイト分のデータをdataに格納しているはずなのですが、
できていないということでしょうか。



    Private Delegate Sub Delegate_RcvDataToTextBox(data As Byte())

    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try
            Dim data(SerialPort1.BytesToRead - 1) As Byte
            SerialPort1.Read(data, 0, 2)

            If SerialPort1.BytesToRead < 2 Then
                '受信データが溜まってない場合は、Read せずにスルー 
                Return
            End If

            Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try





    End Sub

    Private Sub RcvDataToTextBox(data As Byte())

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            'RcvTextBox.AppendText(CStr(data))

            Dim a As Short = BitConverter.ToInt16(data, 0)   '符号付き 2 バイト整数に変換 
            Dim b As String = Convert.ToString(a, 10)  '10進数表記の "1023" に変換 
            RcvTextBox.AppendText(b)
 
        End If

    End Sub 

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/7 09:35:13
> data.lengthを2に設定し、2バイト溜まっていなければスルーする形でテストしました。
data.Length は 2 になっていませんし、スルーする形にもなっていません。


> Dim data(SerialPort1.BytesToRead - 1) As Byte
> SerialPort1.Read(data, 0, 2)
> If SerialPort1.BytesToRead < 2 Then
>   Return

例えば、SerialPort1.BytesToRead が「0」や「1」を返した場合に
どのような動作になるのかを、改めて想像してみてください。


BytesToRead が 「0」だった場合、今のコードだと
 Dim data(-1) As Byte  'これは data.Length = 0 な配列になる
 SerialPort1.Read(data, 0, 2)
に相当する処理が行われることになります。

データが来ていないにも関わらず、Read を呼び出すことになりますし、
0バイトしか確保されていない配列に 2 バイト読み込もうとするので
先の例外が発生してしまうはずです。
検査用の If 文が置いてあるのはその後ですので、既に手遅れです。


一方、BytesToRead が「1」を返すタイミングだった場合には、
 Dim data(0) As Byte  'これは data.Length = 1 な配列になる
 SerialPort1.Read(data, 0, 2)
となるわけですが、これも、データが 1 バイトしか溜まってないうちに
2 バイトを読み込もうとしていることになりますし、
data.Length は 1 しか無いわけなので、当然例外になります。

投稿者 vb素人   (社会人)   投稿日時 2017/7/7 11:45:50
魔界の仮面弁士さま

>例えば、SerialPort1.BytesToRead が「0」や「1」を返した場合に
>どのような動作になるのかを、改めて想像してみてください。

想像してみて少し理解することができました。
Readの位置がおかしな場所にありました。

2つ報告があります。
①次の通り修正し、2バイト揃ってから、2バイト受信する形にしました。(しているつもりです。)

Dim data(SerialPort1.BytesToRead - 1) As Byte

If SerialPort1.BytesToRead < 2 Then
'受信データが溜まってない場合は、Read せずにスルー 
Return
End If
SerialPort1.Read(data, 0, 2)

Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), Data)


データの変換は次の通り行なっています。
    Private Sub RcvDataToTextBox(data As Byte())

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            'RcvTextBox.AppendText(CStr(data))

            Dim bin As Byte() = {data(1), data(0)}
            Dim a As Short = BitConverter.ToInt16(bin, 0)   '符号付き 2 バイト整数に変換 
            Dim b As String = Convert.ToString(a, 10)  '10進数表記の "1023" に変換 
            RcvTextBox.AppendText(b)
            'RcvTextBox.AppendText(BitConverter.ToString(data))
        End If

    End Sub

その結果、「1023」と表示することができましたが、まれに「-253」と表示することがあります。
データ(バイト)の区切りがおかしいのでしょうか。



②これを回避するために、データの始まりを明確にしようと考えました。
送信側から"S"を送信し、
受信側で"S"を受信したら、そこから2バイト取得する形にしてみました。(そのつもりでコードを書いてみました)

しかし、このコードで確認した所、Debug Lineに"?"が表示されてしまいます。
1文字("S")を受信するので、ReadExisting()を使用しているのですが、何か間違いがあるのでしょうか。


            Dim sdata As String
            sdata = SerialPort1.ReadExisting()
            Debug.WriteLine(sdata)

            If sdata = "S" Then

                Dim data(SerialPort1.BytesToRead - 1) As Byte


                If SerialPort1.BytesToRead < 2 Then
                    '受信データが溜まってない場合は、Read せずにスルー 
                    Return
                End If

                SerialPort1.Read(data, 0, 2)
                Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)

            End If



投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/7 13:17:24
> Dim data(SerialPort1.BytesToRead - 1) As Byte
BytesToRead の値を If で検査する前に使っちゃ駄目ですよ。

今回は 2 バイトの固定長データのようなので、たとえば

Dim size As Integer = SerialPort1.BytesToRead
If size < 2 Then
 Return
End If
Dim data(size - 1) As Byte
SerialPort1.Read(data, 0, size)


もしくは

Dim size As Integer = SerialPort1.BytesToRead
If size < 2 Then
 Return
End If
Dim data(1) As Byte
SerialPort1.Read(data, 0, 2)


のように、サイズをチェックしてから配列を確保するようにします。

もし、size が 2 の時は両者で同じ結果が得られますが、
複数のデータがバッファにたまっていて、仮に size が 3 や 4 を
返してきた場合の振る舞いは両者で異なります。適宜使い分けてみてください。


> 「1023」と表示することができましたが、
おめでとうございます。


> 「1023」と表示することができましたが、まれに「-253」と表示することがあります。
それぞれ、こういうバイナリですよね。
1023 = &H03FF
-253 = &HFF03


例えばデータが「03,FF,03,FF,03,FF,…」と流れてきた場合、
どこからどこまでの 2 バイト分を処理するのかによって、
結果が変わってくることになります。


> 受信側で"S"を受信したら、そこから2バイト取得する形にしてみました。
Encoding 設定が ASCII だとしたら、"S" というのは &H53 のことですね。

1023 (&H03FF) の場合は &H53 が含まれないので良さそうですが、
 595 (&H0253) や 21432 (&H53B8) のように、&H53 を含む
バイナリーが送出されてくる可能性は無いのでしょうか。


> 1文字("S")を受信するので、ReadExisting()を使用しているのですが
全ての電文が文字列で構成されているのならば良いですが、
今回は文字として処理できないバイナリーが含まれているので、
ReadExisting は使わない方が良いのでは無いでしょか。

投稿者 vb素人   (社会人)   投稿日時 2017/7/7 17:45:33
魔界の仮面弁士さま

>1023 (&H03FF) の場合は &H53 が含まれないので良さそうですが、
>595 (&H0253) や 21432 (&H53B8) のように、&H53 を含む
>バイナリーが送出されてくる可能性は無いのでしょうか。

おっしゃる通り、今は固定値(1023)で行っていますが、
いずれは、0~1023の値が送出されてきます。

10bit以上の値は送信しないので、65535(FFFF)を受信したら、
そこから2バイト取得する形にしてみました。
(もっと簡単に先頭バイトを見分ける方法があるのかもしれませんが…)

↓のコードで試してみましたが、
「算術演算の結果、オーバーフローが発生しました」とでてしまいました。。。

この方法に無理があるのでしょうか。


            Dim rdat(3) As Byte
            SerialPort1.Read(rdat, 0, 4)
            Dim rdatbin As Byte() = {rdat(3), rdat(2), rdat(1), rdat(0)}
            Dim rdata As Short = CShort(BitConverter.ToInt32(rdatbin, 0))   '符号付き 2 バイト整数に変換 
            Dim rdatb As String = Convert.ToString(rdata, 10)  '10進数表記の "1023" に変換 

            If rdatb = "65535" Then

                Dim size As Integer = SerialPort1.BytesToRead
                If size < 2 Then
                    Return
                End If
                Dim data(size - 1) As Byte
                SerialPort1.Read(data, 0, size)
                Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)

            End If




投稿者 (削除されました)   ()   投稿日時 2017/7/7 19:18:52
(削除されました)

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/7 19:59:37
今回読み取るデータは、「65535 (2バイト)」+「1023 (2バイト)」の
計 4 バイトであるはずです。しかしながら提示頂いたコードでは、
> SerialPort1.Read(rdat, 0, 4)
> SerialPort1.Read(data, 0, size)
という 2 つの Read 処理が記述されています。

これだと、4 バイトではなく、4 + size バイトということで、
少なくとも 6 バイト分を読み取ろうとしていることになりませんか。


たとえば受信バッファ上に 5 バイトのデータ {01,23,45,67,89} があった場合、
たとえば下記のように Read すると、コメント部のような動作になるものだと
思っていたのですが……違いましたっけ?

If SerialPort1.ReadBufferSize >= 5 Then
 Dim bin(7) As Byte           'bin は {00, 00, 00, 00, 00, 00, 00, 00 } になる 
 '受信バッファから 2 バイト分を取り出し、bin(5)~bin(6) の位置に転写する 
 SerialPort1.Read(bin, 5, 2)  'bin は {00, 00, 00, 00, 00, 01, 23, 00 } になる 
 '受信バッファから 1 バイト分を取り出し、bin(3)~bin(3) の位置に転写する 
 SerialPort1.Read(bin, 3, 1)  'bin は {00, 00, 00, 45, 00, 01, 23, 00 } になる 
 'この時点で、まだ受信バッファには {67, 78} の 2 バイトが残留している 
End If





> この方法に無理があるのでしょうか。
> Dim rdat(3) As Byte
> SerialPort1.Read(rdat, 0, 4)

最初に Read メソッドで 4 バイトのデータを取り出そうとしているようですが、
読み出す前には常に、受信バッファにそれだけのデータが溜まっていることを
確認しなければなりません。(今回の場合は BytesToRead ≧ 4 かどうかということ)

受信したデータ以上の長さを読み取るとすると、また先の例外に陥りそうです。。
>>> **配列のオフセット及び長さが範囲を超えているか、カウンターがソースコレクションの
>>> インデックスから最後までの要素の数より大きい値です。**



> 10bit以上の値は送信しないので、65535(FFFF)を受信したら、
> そこから2バイト取得する形にしてみました。
FF が 3 回続くこともありそうですが、大丈夫ですか?

周知とは思いつつ念のために確認しますが、たとえば 1023(&H3FF) が
機器側から送信される場合、"FF,03" と "03,FF" のいずれの順序で
電送される仕様なのか把握されていますでしょうか。

もしも前者なら、たとえば 1023 (FF,03) と 801 (21,03) の 2 つが送信される場合、
"FF,FF,FF,03,FF,FF,23,01" が流れてくることになりますし、
もしも後者なら、たとえば 1023 (03,FF) と 801 (03,21) の 2 つが送信される場合、
"FF,FF,03,FF,FF,FF,03,21" が流れてくることになります。

ということで、どちらのパターンであれ、…,FF,FF,FF,xx,FF…という並びが
含まれる可能性がありえるのではないでしょうか。仮に最初のデータを受信し損ねた場合、
FFFF の読み取り位置がズレて「-253 (&HFF03)」と誤読してしまうかも知れません。

受信漏れが起きたときに、データが欠損する(読み落とす)というのならばともかく、
データが破損する(化ける)というのは都合が悪い気がします。

http://qiita.com/ozwk/items/18d44f38e26ee25ec6e8


> Dim rdatbin As Byte() = {rdat(3), rdat(2), rdat(1), rdat(0)}
ちなみに
 System.Net.IPAddress.HostToNetworkOrder
 System.Net.IPAddress.NetworkToHostOrder
のメソッドを使うと、Short 値や Integer 値のバイナリ順序を逆転できます。

たとえば、
 Dim shortValue As Short = 1023  '&H03FF
 Dim intValue As Integer = 1023  '&H000003FF
という値を上記のメソッドに渡すと、
shortValue は -253 (&HFF03)、intValue は -16580608 (&HFF030000) になります。


> Dim rdata As Short = CShort(BitConverter.ToInt32(rdatbin, 0))   '符号付き 2 バイト整数に変換 
rdatbin のバイナリが FF,FF,xx,xx の 4 バイトであると仮定すると
もしもそれが
FF,FF,00,00 なら、ToInt32 が返す値は 65535 になりますし、
FF,FF,03,FF なら、ToInt32 が返す値は -16515073 になりますし、
FF,FF,FF,03 なら、ToInt32 が返す値は 67108863 になりますし、
FF,FF,FF,FF なら、ToInt32 が返す値は -1 になるわけです。

CShort 型の範囲は -32768~32767 なのですから、大抵の場合オーバーフローする値ですよね。


対処法はいろいろありますが、FF,FF の 2 バイトは、
 BitConverter.ToUInt16 すれば 65535 ( = UShort.MaxValue )
 BitConverter.ToInt16 すれば -1
になる値なので、先導 2 バイトと後続 2 バイトの 2 回に分けて
読み取る方が楽かもしれません。

あるいは BitConverter にかけるのではなく、
rdatbin(n) と rdatbin(n + 1) の両方が &HFF かどうかを
確認すると言う手もあります。

4 バイト丸ごと変換するのなら、BitConverter.ToUInt32 した上で、
ビットマスクもしくはビットシフト演算で取り出すと言う手もあります。

投稿者 vb素人   (社会人)   投稿日時 2017/7/7 22:54:48
魔界の仮面弁士さま

色々勉強させていただき、ありがとうございます。

まず、送信側ですが、次のコード(PICマイコン)で送信しています。
この送信を無限ループで繰り返しています。
WriteUSART(0x00FF);
delay_ms(500);
WriteUSART(0x00FF);
delay_ms(500);

WriteUSART(1023  >> 8);
delay_ms(50);
WriteUSART(1023  & 0x00FF);
delay_ms(500);

まず、これをXCTU(XBEE通信ソフト)で確認すると、
受信データは、FF FF FF 03 となります。(FF FF FF 03が繰り返される)


続いて、vbで作成しているソフトの動作ですが、
アドバイスいただいた通り、4バイト受信して、
rdat(n)と、rdata(n+1)がそれぞれ、&HFFであることを確認する方法にチャレンジしています。

次のコードで、受信バイトにデータが4バイト以上溜まったら、
2バイトずつ読み取ります。
読み取ったデータをDebugLineで確認した所、次の値が表示されます。
255
0
255
0
255
0

3
0

当たり前かもしれませんが、2バイトに変換しているので、0が余計に入ってきてしまいます。
rdat1bin, rdat2binの配列を1バイトに変換するためには、どのようにすれば良いのでしょうか?



If SerialPort1.ReadBufferSize >= 4 Then

Dim rdat1(4) As Byte
SerialPort1.Read(rdat1, 0, 4)
Dim rdat1bin As Byte() = {rdat1(0), rdat1(1)}
Dim rdat2bin As Byte() = {rdat1(2), rdat1(3)}

Dim rdat1a As Short = CShort(BitConverter.ToInt16(rdat1bin, 0))   '符号付き 2 バイト整数に変換 
Dim rdat2a As Short = CShort(BitConverter.ToInt16(rdat2bin, 0))   '符号付き 2 バイト整数に変換 

Debug.WriteLine(rdat1a)
Debug.WriteLine(rdat2a)

End If





投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/8 12:17:24
> WriteUSART(0x00FF);
00FF だと、桁数的に 16bit 値であるような印象を受けました。

google で "WriteUSART" を調べてみたところ
>> void WriteUSART(char data);
という解説がなされていました。
char は 8 bit 値なので、WriteUSART(0xFF); 表記の方が
見た目的には良いのではないでしょうか。(動作結果は同じだと思います)



> rdat(n)と、rdata(n+1)がそれぞれ、&HFFであることを確認する方法にチャレンジしています。
n = 0 で良いのなら、たとえば
 If rdat(0) = &HFF AndAlso rdat(1) = &HFF Then
あるいは、
 If (rdat1(0) * 256 + rdat1(1)) = &HFFFF Then
ということでしょうか。



> Dim rdat1(4) As Byte
これは『Dim rdat1(0 To 4) As Byte』の意味になるので、
5バイト分の領域が確保されます。ご注意ください。


> 受信データは、FF FF FF 03 となります。
> 読み取ったデータをDebugLineで確認した所、次の値が表示されます。

DebugLine というのは、Debug.WriteLine のことかと思いますが、
本当にこれだけのコードで 255, 0, 255, 0, 255, 0, , 3, 0 が
出力されたのでしょうか?

最初に数値が 6 個、次に空行、続けて 数値が 2 つということは、
空行も含めて 計 9 行の出力になっているようですが…。

提示いただいたコードでは、「FF FF FF 03」を受信した後には
> Debug.WriteLine(rdat1a)
> Debug.WriteLine(rdat2a)
の 2 回しか出力されていませんので、各回の出力結果は 2 行だけのはず。



テスト手順を確認させてください。

「FF FF FF 03」が受信された後、ReadBufferSize >= 4 の判定を通過して
Dim rdat1(4) As Byte で確保された配列(5 バイト分の領域)が、
> SerialPort1.Read(rdat1, 0, 4)
により、rdat1 の先頭 4 バイト部分が書き換えられ、
 rdat1 = New Byte() {&HFF, &HFF, &HFF, &H3, &H0}
に相当する内容になるのかな、と想像していましたが、今は違うのですね?

確認のため、SerialPort1.Read(rdat1, 0, 4) の直後に
Debug.WriteLine(BitConverter.ToString(rdat1))
を実行してみてください。

それが本当に "FF-FF-FF-03-00" なのだとしたら、提示いただいたコードの出力結果は
255, 0, 255, 0, 255, 0, , 3, 0 ではなく、-1, 1023 になるはずなのです。



> Dim rdat1bin As Byte() = {rdat1(0), rdat1(1)}
> Dim rdat2bin As Byte() = {rdat1(2), rdat1(3)}
> Dim rdat1a As Short = CShort(BitConverter.ToInt16(rdat1bin, 0))   '符号付き 2 バイト整数に変換 
> Dim rdat2a As Short = CShort(BitConverter.ToInt16(rdat2bin, 0))   '符号付き 2 バイト整数に変換 

昨日のコードでは
> Dim rdatbin As Byte() = {rdat(3), rdat(2), rdat(1), rdat(0)}
と降順に並べていたのに、今度は昇順ですね。どちらが正しい順序でしょうか。

並び順を入れ替える必要が無いのなら、わざわざ 2 バイト単位で詰め直さずとも
 Dim rdat1a As Short = BitConverter.ToInt16(rdat1, 0)  'rdat(0)
 Dim rdat2a As Short = BitConverter.ToInt16(rdat1, 2)
で十分ですよ。負数になると都合が悪いのなら、
 Dim rdat1a As UShort = BitConverter.ToUInt16(rdat1, 0)
 Dim rdat2a As UShort = BitConverter.ToUInt16(rdat1, 2)
でも OK 。

投稿者 vb素人   (社会人)   投稿日時 2017/7/8 15:18:22
魔界の仮面弁士さま

毎回ありがとうございます。

①送信側は、WriteUSART(0xFF); に念の為、修正させていただきました。
動作に変わりはありませんでした。


②続いて、受信データの確認を行いました。
>確認のため、SerialPort1.Read(rdat1, 0, 4) の直後に
>Debug.WriteLine(BitConverter.ToString(rdat1))
>を実行してみてください。

実行結果は、次の通りとなりました。
これは、配列の先頭にデータが毎回入るだけで、配列の要素数(5)に問題はありますが、
配列の要素1~4にデータが入らないのがなぜかわかりません。
FF-00-00-00-00
FF-00-00-00-00
FF-00-00-00-00
03-00-00-00-00

③受信データ確認

教えていただいた所を修正して、次のコードで再び受信データの確認を行いましたが、
やはり昨日と同じ結果です。

255
0
255
0
255
0
3
0


確認したコード

            If SerialPort1.ReadBufferSize >= 4 Then

                Dim rdat1(4) As Byte
                SerialPort1.Read(rdat1, 0, 4)

                'Debug.WriteLine(BitConverter.ToString(rdat1))

                Dim rdat1bin As Byte() = {rdat1(0), rdat1(1)}
                Dim rdat2bin As Byte() = {rdat1(2), rdat1(3)}

                Dim rdat1a As UShort = BitConverter.ToUInt16(rdat1, 0)               '符号付き 2 バイト整数に変換 
                Dim rdat2a As UShort = BitConverter.ToUInt16(rdat1, 2)               '符号付き 2 バイト整数に変換 

                Dim rdat1b As String = Convert.ToString(rdat1a, 10)  '10進数表記の "1023" に変換 
                Dim rdat2b As String = Convert.ToString(rdat1a, 10)  '10進数表記の "1023" に変換 


                Debug.WriteLine(rdat1a)
                Debug.WriteLine(rdat2a)


            End If

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/8 19:20:45
> これは、配列の先頭にデータが毎回入るだけで、配列の要素数(5)に問題はありますが、
> 配列の要素1~4にデータが入らないのがなぜかわかりません。

ReadBufferSize >= 4 ではなく、BytesToRead >=4  にしてみてください。

投稿者 vb素人   (社会人)   投稿日時 2017/7/9 07:18:37


SerialPort1.BytesToRead >= 4
にすることで、配列内に受信データが格納されるようになりました。

Debug.WriteLineで①、②、③の位置でデータを確認していますが、
送信データ(FF FF FF 03が繰り返される)のどこから受信をし始めるかによって
データが変わってしまいます。

Aパターン
①FF-03-FF-FF
②255
③3


Bパターン
①FF-FF-FF-03
②255
③255

今2つ課題が残っています。
1つ目は、
Aパターンの時は、④の"OK"表示まで進みません。
⇒こちらは、今のやり方ではだめだと感じていますが、他に方法が思い浮かびません。

2つ目は、
Bパターンの時は、④の"OK"表示まで進むのですが、
その後、TextBoxにデータが表示されません。
⇒こちらは何が間違えているのかがよく理解できていません。


            If SerialPort1.BytesToRead >= 4 Then

                Dim rdat1(3) As Byte
                SerialPort1.Read(rdat1, 0, 4)

                Debug.WriteLine(BitConverter.ToString(rdat1)) '①

                Dim rdat1bin As Byte() = {rdat1(0), rdat1(1)}
                Dim rdat2bin As Byte() = {rdat1(2), rdat1(3)}

                Dim rdat1a As UShort = BitConverter.ToUInt16(rdat1, 0)  '符号付き 2 バイト整数に変換 
                Dim rdat2a As UShort = BitConverter.ToUInt16(rdat1, 2)  '符号付き 2 バイト整数に変換 

                Dim rdat1b As String = Convert.ToString(rdat1a, 10)  '10進数表記の "1023" に変換 
                Dim rdat2b As String = Convert.ToString(rdat1a, 10)  '10進数表記の "1023" に変換 

                Debug.WriteLine(rdat1(0)) '②
                Debug.WriteLine(rdat1(1)) '③


                If rdat1(0) = &HFF AndAlso rdat1(1) = &HFF Then
                    MessageBox.Show("OK") '④
                    Dim size As Integer = SerialPort1.BytesToRead
                    If size < 4 Then
                        Return
                    End If
                    Dim data(size - 1) As Byte
                    SerialPort1.Read(rdat1, 2, 2)
                    Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)
                End If


            End If



投稿者 YuO   (社会人)   投稿日時 2017/7/9 12:11:02
うーん,パケット区切りがわからない,というのが問題なわけですから,
送信側も受信側も透過モードをやめてAPIモードで送出すれば良い気がしますが……。
もちろん,APIモードでの送受信は透過モードよりもややこしくなりますが,
XBee自体の機能としてパケット区切りがわかるようになる機能があるのですから,自分で実装する必要はないと思います。

一応,透過モードで送ってAPIモードで受信とかその逆も出来ますが,
透過モードだと明示的なパケット区切りがわからないため,両方APIモードで運用する必用があります。

投稿者 (削除されました)   ()   投稿日時 2017/7/9 15:30:23
(削除されました)

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/9 15:32:23
 vb素人さん、最初は(学生)でしたが 2回目以降は(社会人)なんですね。


> SerialPort1.BytesToRead >= 4
> にすることで、配列内に受信データが格納されるようになりました。

BytesToRead は、通信状況に応じて変動しますが、
ReadBufferSize は特に指定しない限り 4096 固定なのです。

当初からずっと、Read 前に BytesToRead を使うように書いておきながら、
間違えて ReadBufferSize な処理を書いてしまった私のミスですね。済みません。



> 送信データ(FF FF FF 03が繰り返される)のどこから受信をし始めるかによって
> データが変わってしまいます。

データの区切りが FF,FF なら、「FF 以外のデータを受信するまで待って、その次に FF が来た位置」
を起点として探すという手はあります。今回のデータは 10bit なので。
あるいは、FF FF を区切りとするのではなく、F2 F0 にしてみるとか。


> Aパターン
> ①FF-03-FF-FF
> Bパターン
> ①FF-FF-FF-03

現行コードは 4 バイト単位で取り込んでいるので、
読み取り開始位置も 4 パターンがありえそうです。

Ⓐパターン:FF-03-FF-FF
Ⓑパターン:FF-FF-FF-03
Ⓒパターン:03-FF-FF-FF
Ⓓパターン:FF-FF-03-FF

本来のデータ構造は Ⓑ なのですが、区切りが FF,FF の場合、
Ⓑ と Ⓓ の違いを区別できません。


しかし区切りを F2,F0 にすれば、上記が以下の形になるため、
ⓑのみが正しいパターンであることが明確になります。

ⓐパターン:FF-03-F2-F0
ⓑパターン:F2-F0-FF-03
ⓒパターン:03-F2-F0-FF
ⓓパターン:F0-FF-03-F2


> If SerialPort1.BytesToRead >= 4 Then
DataReceived イベントの処理中にも、刻一刻とデータは送られてきます。


> Dim rdat1(3) As Byte
> SerialPort1.Read(rdat1, 0, 4)
ここで FF FF FF 03 が正しく受け取られた(Ⓑパターン)なら、
 'Dim rdat1() As Byte = {255, 255, 255, 3}
 If rdat1(0) = &HFF AndAlso rdat1(1) = &HFF Then
  Dim rdat1a As UShort = BitConverter.ToUInt16(rdat1, 2)
  Debug.WriteLine(rdat1a)   '1023
 End If
ということになるのでは?

もちろん都合よくⒷになるわけではないので、位置合わせの対処は必要ですが。


> ⇒こちらは何が間違えているのかがよく理解できていません。
いろいろ間違ってます。

> Dim data(size - 1) As Byte
> SerialPort1.Read(rdat1, 2, 2)
> Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)
変数 data を初期化していますが、Read に渡しているのは data ではなく rdat1 です。
これがまず間違っているでしょうね。


次に、そもそも 2 回目の Read がおかしいです。

仮に Ⓑ パターンだった場合、1023 を意味するデータ &H03FF は
既に rdat1(2) = &HFF および rdat1(3) = &H3 に入っているわけですから、
追加で Read するべきでは無いでしょう。

それに受信データが 4 ~ 7 バイト分だった場合は、
> If size < 4 Then
>   Return
> End If
の箇所で処理が終わってしまいますので、TextBox には到達しません。


仮に、8 バイト以上受信していたとして、かつ運よく、Ⓑパターンだったとしても
① ② ③ ④ ⑤ ⑥ ⑦ ⑧
FF,FF,FF,03,FF,FF,FF,03

のうち、最初の「SerialPort1.Read(rdat1, 0, 4)」では
①②③④の部分が読み込まれ、Invoke 直前の「SerialPort1.Read(rdat1, 2, 2)」で
⑤⑥が読み取られますので、やはりうまくいきません。


最初にヘッダーの 2 バイトだけを読みとり、
それが FF FF の時のみ、次の 2 バイトを読んでいくといった
形にしないと駄目でしょう。

(FF,FF のままだと、先述の区切りの問題も解決しないといけないですが…) 

投稿者 vb素人   (社会人)   投稿日時 2017/7/9 15:39:43
Yuoさま

コメントありがとうございます。
諸事情により透過モードを採用しています。

(送信、受信共にマイコンで本来は制御するものを作成済です。
マイコンで受信するプログラムは完成しているのですが、
送信側の様子をPCで確認するためのツールとして、今回の掲示板に相談しているものを作成しております。)

そのため、面倒くさいかもしれませんが、透過モードでトライしています。
私自身、マイコン(C言語)とvb.netとで混乱している所があり、
初歩的な質問やあり得ない質問などが多くなってしまい、恐縮しているところです。


今、次のコードで意図することができたような気がしています。

※意図する動作
送信側は、FF FF FF 03が繰り返されるのですが、
受信側は、FFを2回受信したら、そこから2バイト受信して、2バイトのデータを10進数に変換。

送信側から送られるデータ(FF 03)が、テキストボックスに1023と表示されることが確認できました。

みなさまから教えていただいたことを振り返りながら、
これから、本当に今のコードで正しいのか検証してみます。
(なんとなく、バグ(FF 03ではなく、FF FFをデータとしてテキストボックスに表示してしまう)がありそうなので、
落ち着いて見直してみます。



Private Delegate Sub Delegate_RcvDataToTextBox(data As Byte())

    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        'シリアルポートをオープンしていない場合、処理を行わない.
        If SerialPort1.IsOpen = False Then
            Return
        End If

        Try

            If SerialPort1.BytesToRead >= 4 Then

                Dim rdat1(3) As Byte
                SerialPort1.Read(rdat1, 0, 2)

                If rdat1(0) = &HFF AndAlso rdat1(1) = &HFF Then
                    MessageBox.Show("OK")
                    Dim data(1) As Byte
                    SerialPort1.Read(data, 0, 2)
                    Debug.WriteLine(BitConverter.ToString(data))
                    Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), data)
                End If


            End If


        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub RcvDataToTextBox(data As Byte())

        '受信データをテキストボックスの最後に追記する.
        If IsNothing(data) = False Then
            'RcvTextBox.AppendText(CStr(data))

            Dim bin As Byte() = {data(0), data(1)}
            Dim a As UShort = BitConverter.ToUInt16(bin, 0)   '符号付き 2 バイト整数に変換 
            Dim b As String = Convert.ToString(a, 10)  '10進数表記の "1023" に変換 
            RcvTextBox.AppendText(b)

        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/10 08:46:37
> 今、次のコードで意図することができたような気がしています。
>> ※意図する動作
>> 送信側は、FF FF FF 03が繰り返されるのですが、
>> 受信側は、FFを2回受信したら、そこから2バイト受信して、2バイトのデータを10進数に変換。

受信開始位置のパケット区切りが分からないという点はどうなりましたか?


>> 送信データ(FF FF FF 03が繰り返される)のどこから受信をし始めるかによって
>> データが変わってしまいます。
>>
>>Aパターン
>>①FF-03-FF-FF
>>Bパターン
>>①FF-FF-FF-03


FF,FF,xx,yy 形式の 4 バイトでデータが送出される形式で、
xx は 00~FF の範囲、yy は 00~03 というのが今回の仕様ですよね。


ということは、FF,FF,xx,yy,FF,FF,xx,yy,…と
連続して送られてくるデータを 4 バイト単位で読む際に
 Ⓐ FF,FF,xx,yy
 Ⓑ FF,xx,yy,FF
 Ⓒ xx,yy,FF,FF
 Ⓓ yy,FF,FF,xx
という 4 パターンのズレ方があるわけです。

今回は xx = FF なわけですから、そうすると今のコードだと
Ⓐ → FF,FF の後の [FF,03] を読み取って 1023
Ⓑ → FF,FF の後の [03,FF] を読み取って 65283
Ⓒ → FF,FF で始まらないので、2 バイトが読み捨てられ、次回からⒶになる
Ⓓ → FF,FF で始まらないので、2 バイトが読み捨てられ、次回からⒷになる
という結果になる気がします。


それを踏まえて、こんな感じでどうでしょうか。
TextBox ではなく、ListBox に表示させています。

'直前の読み残しを保持しておく変数 
Private cache As Byte() = {}
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
If Not SerialPort1.IsOpen Then
    Return
End If

Dim size As Integer = SerialPort1.BytesToRead
If size + cache.Length >= 4 Then
    Dim bin(3) As Byte
    Array.Copy(cache, bin, Math.Min(4, cache.Length))
    SerialPort1.Read(bin, Math.Min(4, cache.Length), Math.Max(0, 4 - cache.Length))
    cache = New Byte() {}

    Dim pattern As Integer = 0
    For n = 0 To 3
        pattern *= 10
        If bin(n) = &HFF Then
            pattern += 1
        End If
    Next

    Dim value As UShort = UShort.MaxValue
    Select Case pattern
        Case 1100, 1110     'Ⓐパターン 
            value = BitConverter.ToUInt16(bin, 2)
            cache = New Byte() {}
        Case 1001, 1101     'Ⓑパターン 
            value = BitConverter.ToUInt16(bin, 1)
            cache = New Byte() {bin(3)}
        Case 11, 1011       'Ⓒパターン 
            value = BitConverter.ToUInt16(bin, 0)
            cache = New Byte() {bin(2), bin(3)}
        Case 110, 111       'Ⓓパターン 
            cache = New Byte() {bin(1), bin(2), bin(3)}
        Case Else           'Ⓔパターン 
            '想定しないデータなので仕切り直し 
            cache = New Byte() {}
    End Select

    If value <> UShort.MaxValue Then
        Invoke(Sub() ListBox1.Items.Insert(0, value))
    End If
End If




投稿者 vb素人   (社会人)   投稿日時 2017/7/10 10:59:38
魔界の仮面弁士さま

最初に学生とした後、その後変えるのを忘れていました。
(そのまま、学生になるものだと思っていました。)
まだ学生の身分です。
不勉強な内容が多く申し訳ありませんが、教えていただいた内容を今すべて復習しています。

開始位置の点は未解決(まだ試していません)で、
↓の内容をまさに実感しながら復習している最中です。
Ⓐ → FF,FF の後の [FF,03] を読み取って 1023
Ⓑ → FF,FF の後の [03,FF] を読み取って 65283
Ⓒ → FF,FF で始まらないので、2 バイトが読み捨てられ、次回からⒶになる
Ⓓ → FF,FF で始まらないので、2 バイトが読み捨てられ、次回からⒷになる

ここまできて、やっと自分が書いているコードと、実際の動作がマッチしてきました。
魔界の仮面弁士さまのおかげです。

復習して納得したあと、
昨日教えていただいた開始位置の解決方法と、
本日教えていただいた直前の読み残しを保存する方法にチャレンジしてみます。

もう少しお時間を下さい。


投稿者 vb素人   (学生)   投稿日時 2017/7/10 17:19:50
魔界の仮面弁士さま

すこし分かったつもりになっていたのか…
なかなか解決できません。

まず、開始位置の見極めとして↓に示すコードでテストしています。
おっしゃる通り④通りのデータを取得する可能性があり、
①のときは、送信データが正しく(1023)TextBoxに表示されますが、
②、③、④のときは、TextBoxにデータが表示されません。

①F2-F0-03-FF
②FF-F2-F0-03
③03-FF-F2-F0
④F0-03-FF-F2

↓のコードの★の部分について。
私の考えでは、先頭バイトがF2-F0でなければ、Returnでスルーしています。
ここまでは、良いと思っていますが、
次にReadしたときも、同じデータの並びになってしまうのでしょうか。

最初にReadしたとき、③だった場合、
★の後、再Readしても③なのでしょうか。
そうだとすると私の考えが浅はかで1バイト毎にシフトさせてReadする? というようなことをしないとだめなのでしょうか。

魔界の仮面弁士様から本日教えていただいた内容はまだ試しておりません。(すみません)


Private Delegate Sub Delegate_RcvDataToTextBox(rdat1 As Byte())

 Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

            If SerialPort1.BytesToRead >= 4 Then

                Dim rdat1(3) As Byte
                SerialPort1.Read(rdat1, 0, 4)
                Debug.WriteLine(BitConverter.ToString(rdat1))

                If rdat1(0) = &HF2 AndAlso rdat1(1) = &HF0 Then
                    'Debug確認用
                    Debug.WriteLine(BitConverter.ToString(rdat1))

                    'rdatをInvoke(起動)
                    Invoke(New Delegate_RcvDataToTextBox(AddressOf Me.RcvDataToTextBox), rdat1)

                    '★
                Else
                    Return
                End If

            End If

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub


    Private Sub RcvDataToTextBox(rdat1 As Byte())

        If IsNothing(rdat1) = False Then

            Dim bin As Byte() = {rdat1(3), rdat1(2)}

            Dim a As UShort = BitConverter.ToUInt16(bin, 0)  '符号なし2 バイト整数に変換 
            Dim b As String = Convert.ToString(a, 10)       '10進数表記の "1023" に変換 
            RcvTextBox.AppendText(b)


        End If

    End Sub

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/10 21:18:14
> ①F2-F0-03-FF
F2-F0-FF-03 だったのでは無いのですか?

WriteUSART(1023  >> 8);      // 0x03
delay_ms(50);
WriteUSART(1023  & 0x00FF);  // 0xFF



なのか

WriteUSART(1023  & 0x00FF);  // 0xFF
delay_ms(50);
WriteUSART(1023  >> 8);      // 0x03



なのかをはっきりさせてください。
10bit 部分がリトルエンディアンなのかビッグエンディアンなのかで
解析手順が微妙に変わります。



前回の FF-FF ヘッダの時は前者でしたので、その前提で
サンプルコードを提示しましたが、今度は後者なのですね?



> 私の考えでは、先頭バイトがF2-F0でなければ、Returnでスルーしています。
> ここまでは、良いと思っていますが、
良くないです。F2-F0 の後ろの 2 バイトを読み捨ててしまっているので、
次回読みだした時も、また F2-F0 になってします。


> 次にReadしたときも、同じデータの並びになってしまうのでしょうか。
毎回、4 バイトずつ読みだしているのでそうなります。
ズレを検知して、次回以降の読み出し開始を変えなければ行けません。




提示いただいたコードの動きを追ってみましょうか。


ここでは話を単純にするため、1000 ミリ秒の等間隔で
 F2-F0-03-FF-F2-F0-03-FF-F2-F0-03-FF-…
とデータが送信されてくるものと仮定します。


まずは①のケースです。(ズレなし)
> ①F2-F0-03-FF
データの送信開始時刻を 10:00:00 とし、
受信もその直後に行われて、ズレが起きていなかった場合、
下記の手順で処理が実行されます。

---------------
10:00:00
 送信側:"F2" を送信。
 受信側:"F2" を受信。バッファの中身は F2 で、BytesToRead は 1。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:01
 送信側:"F0" を送信。
 受信側:"F2" を受信。バッファの中身は F2-F0 で、BytesToRead は 2。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:02
 送信側:"03" を送信。
 受信側:"03" を受信。バッファの中身は F2-F0-03 で、BytesToRead は 3。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:03
 送信側:"FF" を送信。
 受信側:"FF" を受信。バッファの中身は F2-F0-03-FF で、BytesToRead は 4。
 コード:Read で 4 バイト読まれたので、バッファは空になり BytesToRead も 0 になる
  『If rdat1(0) = &HF2 AndAlso rdat1(1) = &HF0 Then』を通るので、
  「03-FF」が Invoke に渡される。

10:00:04
 送信側:"F2" を送信。
 受信側:"F2" を受信。バッファの中身は F2 で、BytesToRead は 1。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。
 ※以下繰り返し
---------------


次は④のケースです。(1 バイトのズレが起きていた場合)
> ④F0-03-FF-F2
---------------
10:00:00
 送信側:"F0" を送信。
 受信側:"F0" を受信。バッファの中身は F0 で、BytesToRead は 1。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:01
 送信側:"03" を送信。
 受信側:"03" を受信。バッファの中身は F0-03 で、BytesToRead は 2。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:02
 送信側:"FF" を送信。
 受信側:"FF" を受信。バッファの中身は F0-03-FF で、BytesToRead は 3。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので処理終了。

10:00:03 ★問題のある部分★
 送信側:"F2" を送信。
 受信側:"F2" を受信。バッファの中身は F0-03-FF-F2 で、BytesToRead は 4。
 コード:Read で 4 バイト読まれたので、バッファは空になり BytesToRead が 0 になる。
  『If rdat1(0) = &HF2 AndAlso rdat1(1) = &HF0 Then』を通らないので処理終了。
 → Invoke に到達しない

10:00:04
 送信側:"F2" を送信。
 受信側:"F2" を受信。バッファの中身は F2 で、BytesToRead は 1。
 コード:『If SerialPort1.BytesToRead >= 4 Then』で無いので Return。
 ※以下繰り返し
---------------

まずいのは、いきなり 4 バイト単位で読みだそうとしている上に、
使わんかったデータを捨ててしまっている点です。

次回以降も、その次の 4 バイトが使われるだけなので、
いつまで経ってもズレを解消できません。
読み出すバイト数は必要最低限にしてください。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/11 01:33:09
F2-F0-xx-yy パターンの処理例を書いてみました。

得られた値が 10bit 範囲(&H0~&H3FF)に納まっているかどうかのチェックは省いています。
また、xx-yy 部のエンディアンが異なる場合には、上位バイトと下位バイトの入れ替えも必要です。


Private Delegate Sub Delegate_RcvDataToTextBox(data As UShort)
Private Sub RcvDataToTextBox(rdat As UShort)
    RcvTextBox.AppendText(Str(rdat))
    'RcvTextBox.AppendText(CStr(rdat)) 
End Sub

Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    If Not SerialPort1.IsOpen Then
        Return
    End If

    If SerialPort1.BytesToRead >= 4 Then
        Dim bin(3) As Byte

        'まずは 2 バイト取り出して、F2,F0 が連続しているか確認する 
        SerialPort1.Read(bin, 0, 2)
        If bin(0) = &HF2 AndAlso bin(1) = &HF0 Then
            'F2-F0-xx-yy パターン 
            SerialPort1.Read(bin, 2, 2) 'もう2バイト追加 
            Dim n As UShort = BitConverter.ToUInt16(bin, 2)
            Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
            Return
        End If

        '3 バイト目を取り出して、F2,F0 が連続しているか確認する 
        SerialPort1.Read(bin, 2, 1)
        If bin(1) = &HF2 AndAlso bin(2) = &HF0 Then
            'yy-F2-F0 パターン。 
            'この yy は利用できないので、Invoke せずに次の受信を待つ 
            Return
        End If

        '4 バイト目を取り出して、F2,F0 が連続しているか確認する 
        SerialPort1.Read(bin, 3, 1)
        If bin(2) = &HF2 AndAlso bin(3) = &HF0 Then
            'xx-yy-F2-F0 パターン 
            Dim n As UShort = BitConverter.ToUInt16(bin, 0)
            Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
            Return
        ElseIf bin(3) = &HF2 AndAlso bin(0) = &HF0 Then
            'F0-xx-yy-F2 パターン 
            Dim n As UShort = BitConverter.ToUInt16(bin, 1)
            Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
            Return
        End If

        '合致パターン無し! 
        Debug.WriteLine("不正なデータ:" & BitConverter.ToString(bin))
    End If
End Sub



投稿者 vb素人   (学生)   投稿日時 2017/7/12 09:37:22
魔界の仮面弁士さま

まず、送信データですが、
F2-F0-03-FFが繰り返し送信されます。(受信も同じ並び)

WriteUSART(1023  >> 8);      // 0x03
delay_ms(50);
WriteUSART(1023  & 0x00FF);  // 0xFF

データの並び順の確認は、
XCTU(XBeeの設定ツール)を使用して確認しました。

少し時間が掛かりましたが、
魔界の仮面弁士さまから教えていただいたコードを一応理解した上で、使ってみました。

Delegateの部分だけ修正させていただきました。
コードを↓に掲載します。

※結果
①先頭検出で失敗(今までは、先頭検出できないと、テキストBoxに受信データが書き込まれない失敗)は無くなりました。ありがとうございます。

②受信データが65283(FF 03)となってしまいます。

送信データは、確かに03 FFの順で送られているのに、なぜここで逆になってしまうのかが理解できません。

③これができた後、F2 F0 x1 x2の後に、もう1つy1 y2を送ることを考えています。
 F2 F0 (先頭検出用データ)
  x1 x2 (X座標データ 0~10bitの範囲)
  y1 y2 (Y座標データ 0~10bitの範囲)
  ②が解決したら、こちらを試してみます。


    Private Delegate Sub Delegate_RcvDataToTextBox(n As UShort)

    Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
        If Not SerialPort1.IsOpen Then
            Return
        End If

        If SerialPort1.BytesToRead >= 4 Then
            Dim bin(3) As Byte

            'まずは 2 バイト取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 0, 2)
            If bin(0) = &HF2 AndAlso bin(1) = &HF0 Then
                'F2-F0-xx-yy パターン 
                SerialPort1.Read(bin, 2, 2) 'もう2バイト追加 
                Dim n As UShort = BitConverter.ToUInt16(bin, 2)
                Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
                Return
            End If

            '3 バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 2, 1)
            If bin(1) = &HF2 AndAlso bin(2) = &HF0 Then
                'yy-F2-F0 パターン。 
                'この yy は利用できないので、Invoke せずに次の受信を待つ 
                Return
            End If

            '4 バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 3, 1)
            If bin(2) = &HF2 AndAlso bin(3) = &HF0 Then
                'xx-yy-F2-F0 パターン 
                Dim n As UShort = BitConverter.ToUInt16(bin, 0)
                Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
                Return
            ElseIf bin(3) = &HF2 AndAlso bin(0) = &HF0 Then
                'F0-xx-yy-F2 パターン 
                Dim n As UShort = BitConverter.ToUInt16(bin, 1)
                Invoke(New Delegate_RcvDataToTextBox(AddressOf RcvDataToTextBox), n)
                Return
            End If

            '合致パターン無し! 
            Debug.WriteLine("不正なデータ:" & BitConverter.ToString(bin))
        End If
    End Sub


    Private Sub RcvDataToTextBox(n As UShort)
        If IsNothing(n) = False Then
            Dim b1 As String = Convert.ToString(n, 10)       '10進数表記の "1023" に変換 
            RcvYTextBox.AppendText(b1)
        End If
    End Sub


投稿者 vb素人   (学生)   投稿日時 2017/7/12 11:18:42
魔界の仮面弁士さま

次の送信データに対応したコードが完成しました。
何回か試していますが、一応できていると思われます。
(1つ前の質問における、データの並び順は解決していませんが、とりあえず配列の順番を変えて対応しています。)

私の理解できる範囲で書いているので、長いコードになってしまっています。


[送信データ]
 F2 F0 (先頭検出用データ)
  x1 x2 (X座標データ 0~10bitの範囲)
  y1 y2 (Y座標データ 0~10bitの範囲)


X, Yのデータ毎にText Boxに表示させています。


    Private Delegate Sub Delegate_RcvXDataToTextBox(xdec As String)
    Private Delegate Sub Delegate_RcvYDataToTextBox(xdec As String)

    Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
        If Not SerialPort1.IsOpen Then
            Return
        End If

        If SerialPort1.BytesToRead >= 6 Then
            Dim bin(5) As Byte

            '① 2 バイト取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 0, 2)
            If bin(0) = &HF2 AndAlso bin(1) = &HF0 Then
                'F2-F0-x1-x2-y1-y2 パターン 
                SerialPort1.Read(bin, 2, 4) 'もう4バイト追加 

                Dim datax As Byte() = {bin(3), bin(2)}
                Dim xbyte As UShort = BitConverter.ToUInt16(datax, 0)    '符号なし2 バイト整数に変換 
                Dim xdec As String = Convert.ToString(xbyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvXDataToTextBox(AddressOf RcvXDataToTextBox), xdec)

                Dim datay As Byte() = {bin(5), bin(4)}
                Dim ybyte As UShort = BitConverter.ToUInt16(datay, 0)    '符号なし2 バイト整数に変換 
                Dim ydec As String = Convert.ToString(ybyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvYDataToTextBox(AddressOf RcvYDataToTextBox), ydec)

                Return
            End If

            '3 バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 2, 1)
            If bin(1) = &HF2 AndAlso bin(2) = &HF0 Then
                Return
            End If

            '4バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 3, 1)
            If bin(2) = &HF2 AndAlso bin(3) = &HF0 Then
                'y1-y2-F2-F0-x1-x2パターン。 
                SerialPort1.Read(bin, 4, 2) 'もう2バイト追加 

                Dim datax As Byte() = {bin(5), bin(4)}
                Dim xbyte As UShort = BitConverter.ToUInt16(datax, 0)    '符号なし2 バイト整数に変換 
                Dim xdec As String = Convert.ToString(xbyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvXDataToTextBox(AddressOf RcvXDataToTextBox), xdec)

                Dim datay As Byte() = {bin(1), bin(0)}
                Dim ybyte As UShort = BitConverter.ToUInt16(datay, 0)    '符号なし2 バイト整数に変換 
                Dim ydec As String = Convert.ToString(ybyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvYDataToTextBox(AddressOf RcvYDataToTextBox), ydec)

                Return
            End If


            '5 バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 4, 1)
            If bin(3) = &HF2 AndAlso bin(4) = &HF0 Then
                'x2-y1-y2-F2-F0(-x1) パターン。 
                Return
            End If




投稿者 vb素人   (社会人)   投稿日時 2017/7/12 11:20:00
1回で掲示できなかったので、続きのコードを掲示します。

            '② 6バイト目を取り出して、F2,F0 が連続しているか確認する 
            SerialPort1.Read(bin, 5, 1)
            If bin(4) = &HF2 AndAlso bin(5) = &HF0 Then
                'x1-x2-y1-y2--F2-F0 パターン 

                Dim datax As Byte() = {bin(1), bin(0)}
                Dim xbyte As UShort = BitConverter.ToUInt16(datax, 0)    '符号なし2 バイト整数に変換 
                Dim xdec As String = Convert.ToString(xbyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvXDataToTextBox(AddressOf RcvXDataToTextBox), xdec)

                Dim datay As Byte() = {bin(3), bin(2)}
                Dim ybyte As UShort = BitConverter.ToUInt16(datay, 0)    '符号なし2 バイト整数に変換 
                Dim ydec As String = Convert.ToString(ybyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvYDataToTextBox(AddressOf RcvYDataToTextBox), ydec)

                Return

                '③
            ElseIf bin(5) = &HF2 AndAlso bin(0) = &HF0 Then
                'F0-x1-x2-y1-y2-F2 パターン 

                Dim datax As Byte() = {bin(2), bin(1)}
                Dim xbyte As UShort = BitConverter.ToUInt16(datax, 0)    '符号なし2 バイト整数に変換 
                Dim xdec As String = Convert.ToString(xbyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvXDataToTextBox(AddressOf RcvXDataToTextBox), xdec)

                Dim datay As Byte() = {bin(4), bin(3)}
                Dim ybyte As UShort = BitConverter.ToUInt16(datay, 0)    '符号なし2 バイト整数に変換 
                Dim ydec As String = Convert.ToString(ybyte, 10)           '10進数表記の "1023" に変換 
                Invoke(New Delegate_RcvYDataToTextBox(AddressOf RcvYDataToTextBox), ydec)

                Return
            End If

            '合致パターン無し! 
            Debug.WriteLine("不正なデータ:" & BitConverter.ToString(bin))
        End If
    End Sub

投稿者 (削除されました)   ()   投稿日時 2017/7/12 12:38:24
(削除されました)

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/12 12:43:27
> WriteUSART(1023  >> 8);      // 0x03
> delay_ms(50);
> WriteUSART(1023  & 0x00FF);  // 0xFF

65238 から 1023 への復元も、同様のビット操作で行えますよ。

Dim x As UShort = 65283
Dim y As UShort = ((x And &HFFus) << 8) + (x >> 8)   '1023 


UShort(UInt16) ではなく Short(Int16) の場合は、
先に紹介したメソッドを利用できます。

>>  System.Net.IPAddress.HostToNetworkOrder
>>  System.Net.IPAddress.NetworkToHostOrder
>> のメソッドを使うと、Short 値や Integer 値のバイナリ順序を逆転できます。

Dim x As Short = -253
Dim y As Short = System.Net.IPAddress.NetworkToHostOrder(x)  '1023 



> ②受信データが65283(FF 03)となってしまいます。

03-FF の順で渡されているので、Windows 上では &HFF03 になります。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim bin As Byte() = {&HFE, &HDC, &HBA, &H98, &H76, &H54, &H32, &H10}

    Debug.Print("データ長{0}バイト:{1}", bin.Length, BitConverter.ToString(bin))

    Debug.WriteLine(" -- ToUInt16 -- ")
    For offset = 0 To bin.Length - 2
        Dim us As UShort = BitConverter.ToUInt16(bin, offset)
        Dim dump As String = BitConverter.ToString(bin, offset, 2)
        Debug.Print("{0}バイト目からの2バイト [{1}] は 0x{2:X4} ({2:N0})", offset, dump, us)
    Next

    Debug.WriteLine(" -- ToUInt32 -- ")
    For offset = 0 To bin.Length - 4
        Dim ui As UInteger = BitConverter.ToUInt32(bin, offset)
        Dim dump As String = BitConverter.ToString(bin, offset, 4)
        Debug.Print("{0}バイト目からの4バイト [{1}] は 0x{2:X8} ({2:N0})", offset, dump, ui)
    Next

    Debug.WriteLine(" -- ToUInt64 -- ")
    For offset = 0 To bin.Length - 8
        Dim ul As ULong = BitConverter.ToUInt64(bin, offset)
        Dim dump As String = BitConverter.ToString(bin, offset, 8)
        Debug.Print("{0}バイト目からの8バイト [{1}] は 0x{2:X16} ({2:N0})", offset, dump, ul)
    Next
End Sub



★実行結果★

データ長8バイト:FE-DC-BA-98-76-54-32-10
 -- ToUInt16 -- 
0バイト目からの2バイト [FE-DC] は 0xDCFE (56,574)
1バイト目からの2バイト [DC-BA] は 0xBADC (47,836)
2バイト目からの2バイト [BA-98] は 0x98BA (39,098)
3バイト目からの2バイト [98-76] は 0x7698 (30,360)
4バイト目からの2バイト [76-54] は 0x5476 (21,622)
5バイト目からの2バイト [54-32] は 0x3254 (12,884)
6バイト目からの2バイト [32-10] は 0x1032 (4,146)
 -- ToUInt32 -- 
0バイト目からの4バイト [FE-DC-BA-98] は 0x98BADCFE (2,562,383,102)
1バイト目からの4バイト [DC-BA-98-76] は 0x7698BADC (1,989,720,796)
2バイト目からの4バイト [BA-98-76-54] は 0x547698BA (1,417,058,490)
3バイト目からの4バイト [98-76-54-32] は 0x32547698 (844,396,184)
4バイト目からの4バイト [76-54-32-10] は 0x10325476 (271,733,878)
 -- ToUInt64 -- 
0バイト目からの8バイト [FE-DC-BA-98-76-54-32-10] は 0x1032547698BADCFE (1,167,088,121,787,636,990)



> 送信データは、確かに03 FFの順で送られているのに、なぜここで逆になってしまうのかが理解できません。

ビッグエンディアンなのかリトルエンディアンなのかということで。
http://www.ertl.jp/~takayuki/readings/info/no05.html



> ③これができた後、F2 F0 x1 x2の後に、もう1つy1 y2を送ることを考えています。

後武運を!

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/12 19:07:04
> (1つ前の質問における、データの並び順は解決していませんが、とりあえず配列の順番を変えて対応しています。)
それで良いとおもいます。


> 何回か試していますが、一応できていると思われます。
提示のコードについて少しだけ。


> Private Sub RcvDataToTextBox(n As UShort)
>  If IsNothing(n) = False Then
この If 文は不要なので削除するべきかと思うのですが、
何を意図した判定文なのでしょうか?

引数 n は構造体(値型)ですので、IsNothing は常に False しか返さないですよ。


> Dim b1 As String = Convert.ToString(n, 10)       '10進数表記の "1023" に変換 
> RcvYTextBox.AppendText(b1)
その出力方法だと、複数のデータが送られてきたときに、
「10」「23」 なのか「102」「3」なのか区別できなくなるのでは。

スペースや改行などで区切って繋いだ方がよろしいかと。

投稿者 vb素人   (学生)   投稿日時 2017/7/13 13:20:20
魔界の仮面弁士さま

>> Dim b1 As String = Convert.ToString(n, 10)       '10進数表記の "1023" に変換 
>> RcvYTextBox.AppendText(b1)
>その出力方法だと、複数のデータが送られてきたときに、
>「10」「23」 なのか「102」「3」なのか区別できなくなるのでは。

>スペースや改行などで区切って繋いだ方がよろしいかと。 

こちらのコメントについて、もう少し教えていただけないでしょうか。
受信データは連続して送られてきていますが、
送信側で、データの最後にCR+LFを入れた方が良いということでしょうか?

最後まで世話が焼けてすみませんが、よろしくお願いします。

投稿者 (削除されました)   ()   投稿日時 2017/7/13 21:26:16
(削除されました)

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2017/7/13 21:34:44
> Private Delegate Sub Delegate_RcvXDataToTextBox(xdec As String)
> Private Delegate Sub Delegate_RcvYDataToTextBox(xdec As String)
デリゲートを 2 つも用意する必要は無いと思います。

Sub RcvXDataToTextBox(xdec As String) と
Sub RcvYDataToTextBox(xdec As String) は、
戻り値の型、および引数の数と型が同一ですし、
デリゲートは 1 つで十分かと。


> 受信データは連続して送られてきていますが、
> 送信側で、データの最後にCR+LFを入れた方が良いということでしょうか?

送信側に入れるという方針もなくは無いですが、ここで提案しているのは
『送信側』ではなく、『受信側』で入れた方が良いのではないか、という意味です。

マイコンで受信するプログラムは完成していて、今回作っているのは
送信側の様子をPCで確認するための監視ツールという位置づけのようですし、
あまり通信仕様をころころ変えるべきでもないでしょう。


Sub RcvXDataToTextBox(xdec As String) や
Sub RcvYDataToTextBox(xdec As String) の実装は分かりませんが
Sub RcvDataToTextBox(data As String)  '5日12時、5日14時、5日17時、5日18時
Sub RcvDataToTextBox(data As Byte())  '6日17時、7日9時、7日11時、9日15時
Sub RcvDataToTextBox(rdat1 As Byte()) '10日17時
などの状況から、現状は下記に近い処理になっているものと推察します。

> Private Sub RcvDataToTextBox(n As UShort)
>  Dim b1 As String = Convert.ToString(n, 10)
>  RcvTextBox.AppendText(b1)
> End Sub
実際には 引数が String になっていたり、TextBox の名前が
変わっていたりもするでしょうが、それはさておき。


今回は 1023 しか送っていませんが、最終的には 10bit のデータとして、
0~1023 の範囲の値が送られてくるわけですよね?

そうすると、前回の受信データと今回の受信データを単純に AppendText するだけでは、
データの区切りが分からなくなってしまうのでは、と懸念しています。

Public Class Form1
    Private Delegate Sub DelegateSample(xdec As UShort)
    Private Sub RcvDataToTextBox(n As UShort)
        Dim b1 As String = Convert.ToString(n, 10)
        RcvYTextBox.AppendText(b1)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Invoke(New DelegateSample(AddressOf RcvDataToTextBox), 1023us)
        Invoke(New DelegateSample(AddressOf RcvDataToTextBox), 1023us)
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Invoke(New DelegateSample(AddressOf RcvDataToTextBox), 102us)
        Invoke(New DelegateSample(AddressOf RcvDataToTextBox), 310us)
        Invoke(New DelegateSample(AddressOf RcvDataToTextBox), 23us)
    End Sub
End Class


Button1 では「1023」を 2 回渡しており、
Button2 では「102」と「310」と「23」という 3 つの値を渡しています。

しかしどちらを使っても、RcvYTextBox に出力される結果は
"10231023" の 8 文字であり、画面上では見分けがつきません。

ですから AppendText の前にデータを補って、" 1023 1023" とか "102,310,23," などと、
何らかの区切りを挿入しておいた方が良いのではないか、という老婆心です。


先の 2017/7/11 01:33:09 の投稿で、
>>    RcvTextBox.AppendText(Str(rdat))
>>    'RcvTextBox.AppendText(CStr(rdat)) 
と、あえて CStr 関数ではなく Str 関数を使っているのもこれが理由です。
rdat が &H3FF(1023) だった場合、
Str(rdat) は " 1023" の 5 文字を返しますが、
CStr(rdat) は "1023" の 4 文字を返しますので。(Convert.ToString も同様)


投稿者 vb素人   (学生)   投稿日時 2017/7/18 11:29:20
魔界の仮面弁士さま

最後の回答について、理解することができました。
少し時間が掛かってしまい、返信が遅れてすみませんでした。

魔界の仮面弁士さまの今までの回答を一通り復習して、今の動作に納得することができました。
ありがとうございます。

今後は、受信したx,yのデータをグラフにプロットできるように頑張ってみます。