質問

だから私はシリアル通信のための通信プロトコルを定義しようとしています、私はデバイスに4バイトの数字を送信することができますが、私はデバイスが右側にそれを拾い始めたことを確認する方法はわかりませんバイト。

例えば送信したい場合は

0x1234abcd 0xabcd3f56 ...
.

どのようにして、デバイスが間違った場所で読み込みを開始していないことを確認し、最初の単語を次のように手に入れます。

0xabcdabcd
.

これをやることの賢い方法はありますか?メッセージの開始のマーカーを使用することを考えましたが、データとして選択した番号を送信したい場合はどうなりますか?

役に立ちましたか?

解決

データが大きい方が大きい方がわかっている場合は、start-of-messageバイトの後にlength-of-dataバイトを送信しないのですか?

あるいは、他のバイナリプロトコルとし、固定されたヘッダを持つパッケージの固定サイズのみを送信します。 4バイトのみを送信すると、実際のデータコンテンツの前に1バイト以上のヘッダーがあることがわかります。

編集:私はあなたが私を誤解していると思います。私が言っているのは、クライアントが、値に基づいてはなく、ストリーム内の位置に基づいて、ヘッダーまたはデータとしてのバイトを常に考慮することになっていることです。 4バイトのデータを送信しているとしたら、1バイトがヘッダーバイトになります。

+-+-+-+-+-+
|H|D|D|D|D|
+-+-+-+-+-+
.

クライアントは、:の行に沿って、かなり基本的な状態マシンになります。

int state = READ_HEADER;
int nDataBytesRead = 0;
while (true) {
  byte read = readInput();
  if (state == READ_HEADER) {
    // process the byte as a header byte
    state = READ_DATA;
    nDataBytesRead = 0;
  } else {
    // Process the byte as incoming data
    ++nDataBytesRead;
    if (nDataBytesRead == 4) 
    {
      state = READ_HEADER;
    }
  }
} 
.

この設定に関することは、byteがヘッダバイトであるかどうかを決定するものが、byteの実際の内容ではなく、ストリーム内の位置であることです。変数のデータバイトを持つ場合は、その後のデータバイト数を示すためにヘッダーに別のバイトを追加してください。このようにして、クライアントがデータであるだけでなくデータストリームのヘッダーと同じ値を送信しているかどうかは問題になりません。

他のヒント

ネットストリング

このアプリケーションでは、おそらく比較的単純な「 NetString 」フォーマットが適切です。

例えば、「こんにちは世界!」というテキストです。として:を符号化します

12:hello world!,
.

空の文字列は3文字としてエンコードします。

0:,
. 一連のバイトとして表すことができる

'0' ':' ','
.

1つのNetString(ネットワークバイト順)を使用して、1つのNetStringの単語0x1234ABCD。別のNetStringでは、一連のバイトとしてエンコードします。

'\n' '4' ':' 0x12 0x34 0xab 0xcd ',' '\n'
'\n' '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'
.

(各NetStringの前後の改行文字 '\ n'はオプションですが、テストとデバッグを容易にします)。

フレーム同期

デバイスが間違ったスポットで読み取られないようにしてください

フレーム同期問題は、一時的なバッファに読み込まれ、私たちは正しい場所で読み始めました。 後で、バッファ内のメッセージの一貫性チェックを実行します。 メッセージが小切手に失敗した場合、何かが間違っていました、 そのため、バッファ内のデータを捨てて、やり直してください。 (重要なメッセージだった場合は、送信機がそれを再送信することを願っています)。 たとえば、シリアルケーブルを最初のNetStringを介して途中に接続されている場合 受信側はバイト文字列を見ます:

0xab 0xcd ',' '\n' '\n'  '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'
.

受信機は ':'を待つのに十分な大きさであるため、次のバイトが有効なデータであることを期待する前に、受信機は最初の部分メッセージを無視してから正しく正しく受信されることができます。

場合によっては、有効なメッセージ長が何になるかを前回知っています。 これにより、受信機が間違った場所で読み始めたことを検出するのがさらに簡単になります。

データとしてメッセージの開始マーカを送信する

私はメッセージの開始のためのマーカーを使うことを考えたが、私がデータとして選択した番号を送りたい場合はどうなるか?

NetStringヘッダを送信した後、送信機は起動していても、メッセージの開始マーカーのようになっていても、RAWデータをAS - IS - IS - に送信します。

通常の場合、Recieverは既にフレーム同期を持っています。 NetStringパーサーはすでに「長さ」と「:」ヘッダーを読んでいます。 そのため、ネットストリングパーサー これらのデータバイトが「:」ヘッダーバイトまたは「フッターバイト」のようになる場合でも、生のデータバイトをバッファ内の正しい場所に直接置きます。

擬似コード

// netstring parser for receiver
// WARNING: untested pseudocode
// 2012-06-23: David Cary releases this pseudocode as public domain.

const int max_message_length = 9;
char buffer[1 + max_message_length]; // do we need room for a trailing NULL ?
long int latest_commanded_speed = 0;
int data_bytes_read = 0;

int bytes_read = 0;
int state = WAITING_FOR_LENGTH;

reset_buffer()
    bytes_read = 0; // reset buffer index to start-of-buffer
    state = WAITING_FOR_LENGTH;

void check_for_incoming_byte()
    if( inWaiting() ) // Has a new byte has come into the UART?
        // If so, then deal with this new byte.
        if( NEW_VALID_MESSAGE == state )
            // oh dear. We had an unhandled valid message,
            // and now another byte has come in.
            reset_buffer();
        char newbyte = read_serial(1); // pull out 1 new byte.
        buffer[ bytes_read++ ] = newbyte; // and store it in the buffer.
        if( max_message_length < bytes_read )
            reset_buffer(); // reset: avoid buffer overflow

        switch state:
            WAITING_FOR_LENGTH:
                // FIXME: currently only handles messages of 4 data bytes
                if( '4' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_COLON;
            WAITING_FOR_COLON:
                if( ':' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_DATA;
                    data_bytes_read = 0;
            WAITING_FOR_DATA:
                // FIXME: currently only handles messages of 4 data bytes
                data_bytes_read++;
                if( 4 >= data_bytes_read )
                    state = WAITING_FOR_COMMA;
            WAITING_FOR_COMMA:
                if( ',' != newbyte )
                    reset_buffer(); // doesn't look like a valid message.
                else
                    // otherwise, it looks good -- move to next state
                    state = NEW_VALID_MESSAGE;

void handle_message()
    // FIXME: currently only handles messages of 4 data bytes
    long int temp = 0;
    temp = (temp << 8) | buffer[2];
    temp = (temp << 8) | buffer[3];
    temp = (temp << 8) | buffer[4];
    temp = (temp << 8) | buffer[5];
    reset_buffer();
    latest_commanded_speed = temp;
    print( "commanded speed has been set to: " & latest_commanded_speed );
}

void loop () # main loop, repeated forever
    # then check to see if a byte has arrived yet
    check_for_incoming_byte();
    if( NEW_VALID_MESSAGE == state ) handle_message();
    # While we're waiting for bytes to come in, do other main loop stuff.
    do_other_main_loop_stuff();
.

その他のヒント

シリアル通信プロトコルを定義するとき プロトコルが常に任意のバイナリ値ではなく、常に人間が読めるASCIIテキスト文字を使用している場合、テストとデバッグを簡単にします。

フレーム同期(再び)

私はメッセージの開始のためのマーカーを使うことを考えたが、私がデータとして選択した番号を送りたい場合はどうなるか?

Reieverがすでにフレーム同期を持っている場合を既にカバーしています。 受信機がまだフレーム同期を持っていない場合はかなり面倒です。

最も簡単な解決策は、送信機が一連の無害なバイトを送信するためのものです。 (おそらく改行またはスペースの文字)、 可能な最大有効メッセージの長さの長さ 各ネットストリングの直前にプリアンブルとして。 シリアルケーブルが接続されているときに受信機がどのような状態にあるかに関係なく、 それらの無害なバイトは最終的に受信機を駆動する "waiting_for_length"状態。 そしてTranmitterがパケットヘッダーを送信したとき(長さに続く ":") 受信機はそれをパケットヘッダとして正しく認識し、フレーム同期を回復しました。

(THには実際には必要ありません

パケットごとにそのプリアンブルを送信する送信機。 おそらくトランスミッタは20個のパケットのうち1分の1に送信できます。次に、受信機は、シリアルケーブルが接続された後、20個のパケット(通常は少なく)でフレーム同期を回復することが保証されています。

その他のプロトコル

他のシステムは、NetStringフォーマットが検出できない多くの種類のエラーを検出するために単純なFletcher-32チェックサムを使用しています( B ) プリアンブルなしでも同期することができます。

多くのプロトコルは、特別な「パケットの始動」マーカーを使用し、送信したい実際のデータであっても、送信データに実際にリテラルの「パケットの開始」バイトを実際に送信するのを防ぐために、さまざまな「エスケープ」テクニックを使用します。その価値を持つようになります。 (一貫したオーバーヘッドバイト詰めビットスタッフィング引用符そして他の種類のバイナリ対テキストエンコーディングなど)。

これらのプロトコルは、「パケットの開始」マーカーが表示されたときに、それが実際のパケットの始まりであることを確認することができるという利点があるという利点があることが、実際のパケットの開始(一部のデータバイトが同時に同じ値にする)です。 これにより、同期の取り扱い損失がはるかに簡単になります。次の「パケットの開始」マーカーまでのバイトを廃棄します。

NetStringフォーマットを含む他の多くのフォーマットで、可能なバイト値をデータとして送信できます。 そのため、受信機は、が実際のヘッダーの始まり、またはがデータバイトになることができる、またはになることができる、またはにする必要がある。最悪の場合には、最悪の場合に「エスケープ」または驚くほど大きなバッファを扱う必要はありません。

1つのアプローチの選択は、他の方法よりも簡単ではありません。 「NOREFERRER」>ウォーターベッド理論

シリアルプログラミングWikibook 、 そしてその本をより良くするためにその本を編集する?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top