web-dev-qa-db-ja.com

シンプルなシリアルポイントツーポイント通信プロトコル

2つのデバイス(PCとマイクロコントローラー)間の単純な通信プロトコルが必要です。 PCはいくつかのコマンドとパラメーターをマイクロに送信する必要があります。マイクロはバイトの配列(センサーからのデータ)を送信する必要があります。

データは noise protected である必要があります(パリティチェック以外に、他のデータ修正方法が必要だと思います)。

これを行うための標準的なソリューションはありますか? (完全なソリューションではなく、アイデアだけが必要です)。

追伸どんなアドバイスも大歓迎です。 P.P.S文法の間違いで申し訳ありませんが、ご理解いただければ幸いです。

編集1。それがになるかどうかは決めていませんmaster/slaveプロトコルまたは両側が通信を開始できます。 PCは、マイクロがいつジョブを実行し、データを送信できるかを知る必要があります。ジョブの完了時に、データの準備ができている場合は継続的にマイクロをポーリングするか、データを送信できます。私はどちらがより良くて簡単かわかりません。

編集2。ハードウェアおよび 物理層 protocol。RS-232 Cシリアル規格がPCで使用されているため、 非同期通信を使用します 。 RxD、TxD、GND信号のみを使用します。マイクロコントローラAFAIKがそれらをサポートしていないため、追加のワイヤを使用できません。ところで私は [〜#〜] avr [〜#〜] ATmega128チップを使用しています。

そのため、固定ボーレート、8ビットのデータ、2ストップビットをパリティチェックなしで使用します(または使用しますか?)。

データリンクプロトコル。それが私の質問が主に懸念していることです。 [〜#〜] hdlc [〜#〜][〜#〜] ppp [〜#〜] を提案してくれてありがとうおよび Modbus プロトコル。私はそれについて研究します。

55
Vanuan

[〜#〜] hdlc [〜#〜] を使用します。私は過去にそれで幸運を持っていました。ポイントツーポイントのシリアルでは、 非同期フレーミング を使用するだけで、他のすべてのコントロールはおそらくやりすぎになるので忘れてしまいます。

パケットのフレーミングにHDLCを使用することに加えて。次のようにパケットをフォーマットします。これは、802.11を使用してオプションを渡す方法です。

U8 cmd;
U8 len;
u8 payload[len];

各コマンドパケットの合計サイズはlen +2です

次に、次のようなコマンドを定義します

#define TRIGGER_SENSOR 0x01
#define SENSOR_RESPONSE 0x02

もう1つの利点は、新しいコマンドを追加でき、未定義のコマンドを無視するようにパーサーを正しく設計すると、下位互換性が得られることです。

したがって、パケットをすべてまとめると、次のようになります。

 // total packet length minus flags len+4
 U8 sflag;   //0x7e start of packet end of packet flag from HDLC
 U8 cmd;     //tells the other side what to do.
 U8 len;     // payload length
 U8 payload[len];  // could be zero len
 U16 crc;
 U8 eflag;   //end of frame flag

次に、システムはシリアルストリームのフラグ0x7eを監視し、そこにあるときに長さがpklen> = 4およびpklen = len + 4であり、crcが有効であることを確認します。小さなパケットの場合はcrcだけに依存しないでください。多くの誤検知が発生し、長さもチェックされます。長さまたはcrcが一致しない場合は、長さとcrcをリセットして、新しいフレームのデコードを開始します。一致する場合は、パケットを新しいバッファーにコピーし、コマンド処理機能に渡します。フラグを受信したら、長さとcrcを常にリセットします。

コマンド処理関数では、cmdとlenを取得し、スイッチを使用して各タイプのコマンドを処理します。また、システムがイベント駆動型のリモートプロシージャコールのように動作するように、特定のイベントが応答を送信する必要があります。

そのため、たとえば、センサーデバイスはタイマーを持っているか、コマンドに応答して読み取りを行うことができます。次に、パケットをフォーマットしてPCに送信し、PCはパケットを受信したと応答します。そうでない場合、センサーデバイスはタイムアウトで再送信できます。

また、ネットワーク転送を行うときは、 OSI modle as Foredecker のようなネットワークスタックとして設計する必要があります 物理層のものを忘れないでください 。 HDLCを使用した私の投稿は データリンクレイヤー および RPCおよびコマンド処理はアプリケーションレイヤー です。

37
Rex Logan

RS232プロトコルには注意が必要です。 HDLCを使用することをお勧めしますが、ソリューション全体ではありません。決定する必要がある他の事柄があります:

  • 2つのデバイス間のボーレートはどのように決定されますか? Autobuad?事前に定義されていますか、または設定されていますか?
  • ソフトウェアまたはハードウェア、あるいはその両方でフロー制御を行いますか?ハードウェアフロー制御を使用する場合、ケーブルが正しく構築されていることをmust確認してください。
  • ケーブルといえば、これはRS233での大きな苦痛です。デバイスによっては、ストレートケーブル、クロスケーブル、またはバリアントを使用する必要がある場合があります。
  • ソフトウェアベースのフロー制御メカニズムを使用すると、最も単純なケーブル(TX、RX、および共通)を3本だけ使用できるため、効果的です。
  • あなたは7または8ビットの単語を選びますか?
  • ハードウェアパリティまたはソフトウェアエラーチェック。

8データビット、ハードウェアパリティなし、1ストップビットを使用し、ソフトウェアベースのフロー制御を使用することをお勧めします。ハードウェアがサポートしている場合は、自動ボーを使用する必要があります。そうでない場合、オートボーはソフトウェアで行うのが非常に困難です。

10
Foredecker

ここにいくつかの良い答えがあります、ここにいくつかの有用なポインタがあります:

パケットが時間で区切られていなくても、同期バイトは、パケットを構築しようとする場所の数を減らすための重要な方法です。多くの場合、デバイスは大量のジャンクデータ(つまり、オンになったときの飛行中のパケットの終わり、またはハードウェアの衝突の結果)を処理する必要があります。同期バイトがないと、受信するすべてのバイトからパケットを作成する必要があります。同期バイトは、ランダムノイズの1/255バイトのみがパケットの最初のバイトになる可能性があることを意味します。また、プロトコルをスヌープしたい場合にも素晴らしいです。

パケットにアドレスを付けたり、マスター/スレーブまたはPC /デバイスと言ったりするだけでも、何らかのタイプの スヌープツール でパケットを見ると便利です。これを行うには、PCのデバイスとは異なる同期バイトを使用します。また、これはデバイスがそれ自身のエコーに応答しないことを意味します。

エラー修正( Hamming など)を検討することもできます。 8ビットのデータを12ビットの保護されたバイトにパッケージ化します。これらの12ビットのいずれかを途中で反転し、元の8ビットを取得できます。データストレージ(CDで使用)またはデバイスが簡単に再送信できない場所(衛星リンク、一方向rf)に役立ちます。

パケット番号は生​​活を楽にします。送信されたパケットには番号が付き、応答には同じ番号が付き、「応答」を示すフラグが付きます。つまり、到着したことのないパケット(同期が破損しているなど)は送信者によって簡単に検出され、低速リンクの全二重モードでは、最初の応答を受信する前に2つのコマンドを送信できます。これにより、プロトコル分析も容易になります(サードパーティは、基礎となるプロトコルを知らなくても、どのパケットが受信されたかを理解できます)

マスターを1つにすることは、非常に簡単です。とはいえ、全二重環境ではそれほど重要ではありません。電力を節約しようとする場合、またはデバイス側で何らかのイベントドリブンを行う場合(入力状態が変更され、サンプルの準備ができている場合)を除き、常に実行する必要があると言えば十分です。

8
Tom Leys

私は数ヶ月前にこの質問を読んで、まったく同じ問題を抱えていましたが、小さなRAMを搭載した小さな8ビットマイクロには十分な効率性を見つけることができませんでした。 CANとLINに触発されて、仕事をするために何かを作りました。私はそれをMIN(マイクロコントローラー相互接続ネットワーク)と呼び、GitHubにアップロードしました:

https://github.com/min-protocol/min

そこには2つの実装があります。1つは組み込みC、もう1つはPythonです。さらに、PCがコマンドを送信し、ファームウェアがLEDを点灯する小さな「hello world」テストプログラムです。これをArduinoボード上で実行する方法については、次をご覧ください。

https://kentindell.wordpress.com/2015/02/18/micrcontroller-interconnect-network-min-version-1-0/

MINは非常に単純です。レイヤー0表現(8データビット、1ストップビット、パリティなし)を修正しましたが、ボーレートは開いたままにしました。各フレームは3つの0xAAバイトで始まります。これはバイナリで1010101010であり、一端が他端に動的に適応したい場合に自動ボーレート検出を行うNiceパルストレインです。フレームは0〜15バイトのペイロードであり、16ビットのフレッチャーのチェックサムと、制御バイトおよび8ビットの識別子(アプリケーションにペイロードデータに含まれる内容を伝えるため)があります。

プロトコルは文字スタッフィングを使用するため、0xAA 0xAA 0xAAは常にフレームの開始を示します。これは、デバイスがリセットから抜けると、常に次のフレームの開始と同期することを意味します(MINの設計目標は、不完全または誤ったフレームを渡すことではありませんでした)。これはまた、特定のバイト間およびフレーム間タイミング制約を持つ必要がないことも意味します。プロトコルの詳細はGitHubリポジトリwikiにあります。

MINには今後の改善の余地があります。ブロックメッセージの受け渡し(制御バイトの4ビットが予約されている)および機能の上位レベルのネゴシエーション(識別子0xFFが予約されている)のためにいくつかのフックを残しているため、一般的に必要な機能のサポートを追加する余地が十分にあります。

5
Ken Tindell

私の提案はmodbusです。これは、センサーとパラメーターを備えたデバイス(PLCなど)との通信用の効率的で簡単な標準プロトコルです。仕様は http://www.modbus.org で入手できます。 1979年以来存在しており、人気が高まっています。サンプルやライブラリを見つけるのに問題はありません。

5
Martin Liesén

代替プロトコルは次のとおりです。

u8  Sync          // A constant value which always marks the start of a packet
u16 Length        // Number of bytes in payload
u8  Data[Length]  // The payload
u16 Crc           // CRC

PC(シリアルポート)とプロセッサ(UART)は既に最小限の手間で処理できるので、RS232/UARTを使用します(レベルシフトを行うには MAX232 チップなどが必要です)。

また、RS232/UARTを使用すると、関係ない場合にマスター/スレーブを心配する必要がありません。必要に応じてフロー制御が利用可能です。

推奨されるPCソフトウェア:独自のソフトウェアを作成するか、単純な監視と制御のために Docklight (評価版は無料)。

エラーチェックを強化するには、パリティチェックが最も簡単です。または、より強力なものが必要な場合は、 convolutional coding とします。

いずれにせよ、あなたが何をするにしても:シンプルにしてください!

EDIT:PCでのRS232の使用は、USBからRS232/TTLへのコンバーターを入手できるため、以前よりもさらに簡単になりました。一端がPCのUSBソケットに接続され、通常のシリアルポートとして表示されます。もう1つは、レベルシフトを必要とせずに、プロセッサに直接接続できる5 Vまたは3.3 V信号になります。

FDTI Chipの TTL-232R-3V を使用しました。これはこの種のアプリケーションに最適です。

3
Steve Melnikoff

私の唯一の提案は、耐ノイズ性が必要な場合、全二重RS-422/485を使用することです。 AVR側で this に似たICを使用し、PC側で the 485PTBR のようなRS-232-> RS-422コンバーターを使用できます。シールドケーブル(ツイストシールドペア2つ)を見つけるか、作成できる場合は、さらに保護が強化されます。そして、これらすべてはマイクロとPCには見えません-ソフトウェアの変更はありません。

何をするにしても、全二重システムを使用していることを確認し、読み取り/書き込み有効化ラインがICでアサートされていることを確認してください。

2

Telemetry とそれに関連するデスクトップ実装をpython Pytelemetry で見ることができます。

主な特徴

これはPubSubベースのプロトコルですが、MQTTとは異なり、ポイントツーポイントプロトコル、ブローカーなしです。

Pubsubプロトコルと同様に、publishtopicの一端から受け取り、そのトピックの他端から通知を受けることができます。

埋め込み側では、トピックへの公開は次のように簡単です:

publish("someTopic","someMessage")

数字の場合:

publish_f32("foo",1.23e-4)
publish_u32("bar",56789)

変数を送信するこの方法は制限されているように見えるかもしれませんが、次のマイルストーンでは、次のようなことを行うことでトピックの解析に意味を追加する予定です。

// Add an indexing meaning to the topic
publish("foo:1",45) // foo with index = 1
publish("foo:2",56) // foo with index = 2

// Add a grouping meaning to the topic
publish("bar/foo",67) // foo is under group 'bar'

// Combine
publish("bar/foo:45",54)

これは、配列、複雑なデータ構造などを送信する必要がある場合に便利です。

また、PubSubパターンは柔軟性があるため素晴らしいです。マスター/スレーブアプリケーション、デバイス間デバイスなどを構築できます。

Cライブラリ GitHub version

Cライブラリは、適切なUARTライブラリがあれば、新しいデバイスに簡単に追加できます。

TM_transportTelemetryで定義)というデータ構造をインスタンス化し、4つの関数ポインターreadreadablewritewriteableを割り当てるだけです。

// your device's uart library function signatures (usually you already have them)
int32_t read(void * buf, uint32_t sizeToRead);
int32_t readable();
int32_t write(void * buf, uint32_t sizeToWrite);
int32_t writeable();

Telemetryを使用するには、次のコードを追加するだけです

// At the beginning of main function, this is the ONLY code you have to add to support a new device with telemetry
TM_transport transport;
transport.read = read;
transport.write = write;
transport.readable = readable;
transport.writeable = writeable;

// Init telemetry with the transport structure
init_telemetry(&transport);  

// and you're good to start publishing
publish_i32("foobar",...

Pythonライブラリ PyPI version

デスクトップ側には、プロトコルを実装するpytelemetryモジュールがあります。

Pythonを知っている場合、次のコードはシリアルポートに接続し、トピックfooで1回公開し、3秒以内に受信したすべてのトピックを出力して終了します。

import runner
import pytelemetry.pytelemetry as tm
import pytelemetry.transports.serialtransport as transports
import time

transport = transports.SerialTransport()
telemetry = tm.pytelemetry(transport)
app = runner.Runner(transport,telemetry)

def printer(topic, data):
    print(topic," : ", data)

options = dict()
options['port'] = "COM20"
options['baudrate'] = 9600

app.connect(options)

telemetry.subscribe(None, printer)
telemetry.publish('bar',1354,'int32')
time.sleep(3)

app.terminate()

Pythonがわからない場合は、コマンドラインインターフェイスを使用できます。

Pytelemetry CLI PyPI version

コマンドラインは

pytlm

その後、connectls(list)が受信したトピック、printがトピックを受信、pub(publish)をトピックで開くか、トピックでplotを開いて、受信したデータをリアルタイムで表示できます。

enter image description here

enter image description here

2
Overdrivr

パリティチェックについて(ここで何度か取り上げているように):

ほとんど役に立たない。 1つのビットが誤って変更される可能性がある場合は、2番目のビットも変更される可能性が高く、パリティチェックから誤検知が発生します。

ルックアップテーブルを備えたCRC16のような軽量のものを使用します。各バイトが受信されると計算でき、基本的には単なるXORです。 Steve Melnikoffの提案は、小さなマイクロに最適です。

生のバイナリではなく、人間が読めるデータを送信することもお勧めします(パフォーマンスが最優先事項でない場合)。デバッグとログファイルがより快適になります。

1
Peter Gibson

たぶん、この質問は完全に愚かかもしれませんが、誰かが X/Y/Z MODEM プロトコルのいずれかの使用を検討しましたか?

上記のプロトコルのいずれかを使用する主な利点は、さまざまなプログラミング環境ですぐに使用できる実装が非常に高いことです。

0

マイクロコントローラーの動作を正確に指定するわけではありませんが、マイクロから送信されるものはすべて、PCからのコマンドに対する直接の応答になりますか?その場合、何らかの種類のマスター/スレーブプロトコルを使用できるように見えます(これは通常、最も簡単なソリューションです)。両側が通信を開始できる場合、より一般的なデータリンク層プロトコルが必要です。 [〜#〜] hdlc [〜#〜] はこのための古典的なプロトコルです。完全なプロトコルはおそらくあなたのニーズには行き過ぎですが、たとえば、少なくとも同じフレーム形式を使用することができます。 [〜#〜] ppp [〜#〜] を見て、何か有用な部分があるかどうかを確認することもできます。

0
hlovdal