• ベストアンサー

パケット通信

C言語初心者です。 H8が乗っているコントローラとシリアル(RS232C)で繋がっている機器間で、 パケットを構成し通信したいのですが、そのパケットの快適な構成方法がわかりません。(コントローラ側のプログラムを作っています) イメージとしては構造体を使用すると快適なのかな?と思うのですが、 よくわかりません。 機器側のパケットは仕様があり、 スタートコード(2)-データ長(1)-データ長チェックサム(1)-データ(n)-データチェックサム(1) のような構成になっています。 ( )はバイト数です。 バイナリデータでの通信です。 データの先頭にコマンドコードがあり、機器の制御をするような感じです。 例えばですが、 typedef struct { unsigned char start[2];----->スタートコード(固定) unsigned char len;---------->データ長(変動) unsigned char len_cs;------->データ長チェックサム(計算) unsigned char data[SIZE];--->データの中身(変動) unsigned char data_cs;------>データチェックサム(計算) } packet; というようなやり方で、固定データや変動するデータを構造体として 使用することはできるのでしょうか? unsigned char cmd[] = {0x00,0x00,・・・・0x00} のように強引にスタートコードからデータ、チェックサムを 配列で並べて送信すると機器はちゃんと動作します。 (チェックサムの計算も電卓で計算してから書いてます) ただ、コマンドごとにこんなパケットを構成しないといけない のは手間ですし、何かブサイクに思えます。 快適なパケット作成の方法があればぜひ教えて下さい。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • t4t
  • ベストアンサー率55% (47/84)
回答No.2

dataが固定長ならよいのですが、可変長データの場合はdata_cs で破綻すると思います。 #pragma pack(1) typede struct { unsigned char start[2]; /*----->スタートコード(固定) */ unsigned char len; /* ---------->データ長(変動) */ unsigned char len_cs; /* ------->データ長チェックサム(計算) */ } packet_header; unsigned char buffer[十分大きな値]; /* ここに実際のコマンド配列が入る */ packet_header* header_pointer = buffer; /* ヘッダ部の先頭ポインタをコマンド列の先頭に合わせる */ unsigned char* data_pointer; unsigned char* data_cs_pointer; header_pointer->start[0] = スタートコード1つめ; header_pointer->start[1] = スタートコード2つめ; header_pointer->len = データ長; header_pointer->len_cs = データ長チェックサムを計算していれる; /* データ格納の開始位置は、ヘッダ部の直後なのでbuffer先頭からヘッダ部サイズだけずらす */ data_pointer = buffer + sizeof(packet_header); /* データを格納する 下記の例では愚直に1つずつ入れてますが… */ data_pointer[0] = データ1つめ; data_pointer[1] = データ2つめ; ...; data_pointer[n] = データnつめ; /* チェックサム格納位置は、データの末尾なので、データ格納の先頭位置からデータ長だけずらした場所になる */ data_cs_pointer = data_pointer + header_pointer->len; data_cs_pointer[0] = データチェックサム; という感じでいかがでしょうか。 コマンドの種類が少なくて、固定的なコマンド列で済むのでしたら、全部配列でもありだと思います。 あと、wolf03さんが指摘しているように、構造体のメンバの間は、コンパイラによっては隙間があきます。 これはプロセッサによってはそういうふうに配置したほうがアクセスが高速なために、コンパイラが気を 利かせて最適化してくれるのです。 そのため、コンパイラに渡すスイッチや、#pramgaなどで、構造体メンバの隙間を埋めることができる コンパイラが多いので、これをあわせて指定してください。 上の例では#pragma pack(1)とかしてますが、この指定はコンパイラによって異なりますので、 コンパイラのマニュアルを確認してください。

man_u
質問者

お礼

遅くなりましたが回答ありがとうございます。 わかりやすい内容で助かりました。 まだキレイに出来ないですが、もうちょっとがんばってみます。

その他の回答 (2)

  • tokichim
  • ベストアンサー率42% (88/205)
回答No.3

このくらいの通信だと、可変長のデータ送信部を無理に構造体で行おうとしても面倒になるだけです。ときにはアラインメントやエンディアンを考慮する必要もありますから、バイト単位の配列に入れてしまう方がよほどすっきりします。 ただし、関数インタフェースとしては構造体を使う意味はあります。 例としてはこんな感じ。(エラー処理もろもろは省く) 使うときはインタフェース構造体に必要情報を入れて、送信関数を呼び出します。 送信関数の中ではバイト配列に直書きして送り出します。 実際のコーディングではパケット構造などはコメントに記すなどしてわかりやすくします。 typedef struct {  unsigned short start;----->スタートコード(2バイト)  unsigned char len;---------->データ長  unsigned char *data;--->データへのポインタ } packet_t; void send_data( packet_t *pkt ) {  unsigned char send_buf[MAX_SIZE];  send_buf[0] = (pkt->start >> 8) & 0xff;  send_buf[1] = (pkt->start >> 0) & 0xff;  send_buf[2] = pkt->len;  send_buf[3] = datalen_chksum(pkt->len); /*計算*/  memcpy( &send_buf[4], pkt->data, pkt_len );  send_buf[4+pkt_len] = data_chksum(pkt->len, pkt->data); /*計算*/  送信処理; }

man_u
質問者

お礼

遅くなりましたが回答ありがとうございます。 わかりやすい内容で助かりました。 まだキレイに出来ないですが、もうちょっとがんばってみます。

  • wolf03
  • ベストアンサー率22% (241/1086)
回答No.1

コンパイラが管理するブロックサイズの問題でしょう。 構造体内の変数の大きさがこの境界に合わない場合に、境界に合わせるための無駄データが組み込まれます。 そのために相手機器が正しく受け取れないのでしょう。 コンパイラのコンパイルオプションか、ソースに埋め込むオプションコマンドで境界サイズを1にしてやれば無駄が埋め込まれなくなります。

man_u
質問者

補足

ご回答ありがとうございます。 通信できる、できない以前の話でして、 構造体にしての組み方がよくわからないのです。 組み方というか・・・ 各メンバーへの代入の仕方というか・・・