• ベストアンサー

パケット通信

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} のように強引にスタートコードからデータ、チェックサムを 配列で並べて送信すると機器はちゃんと動作します。 (チェックサムの計算も電卓で計算してから書いてます) ただ、コマンドごとにこんなパケットを構成しないといけない のは手間ですし、何かブサイクに思えます。 快適なパケット作成の方法があればぜひ教えて下さい。 よろしくお願いします。

  • man_u
  • お礼率70% (56/80)

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

  • ベストアンサー
  • 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
質問者

補足

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

関連するQ&A

  • 1バイトデータの読み出しについて

    こんばんは。 すみません。文字操作?ポインタ操作が苦手なので教えてください。 以下のような構造体があり、その構造体から1バイトずつ読み出して 自作の関数のパラメータ(1バイト指定領域)に渡したいのですが、 構造体から1バイトずつデータを読み出すには、どのようにすればいいのでしょうか? typedef struct { PACKET_HEADER head; unsigned char Sp_Pr_St[1]; unsigned char ctrl_flow[1]; unsigned char product_org[10]; unsigned char product_name[10]; unsigned char product_ver[3]; }PRODUCT_RESPONS; typedef struct { unsigned char head; unsigned char type; unsigned char data_len; } HEADER; どうぞよろしくお願いします。

  • javaのbyte配列へintなどを埋め込む方法

    javaのbyte配列へintなどを埋め込む方法 java初心者です。データ編集がわからなくて悩んでいます。 下記のコードは"棒読みちゃん"といソフトへのTCPパケットを作成するコードです。 char buf[15]; *((short*)&buf[0]) = 0x0001; *((short*)&buf[2]) = speed; *((short*)&buf[4]) = tone; *((short*)&buf[6]) = volume; *((short*)&buf[8]) = voice; *((char* )&buf[10]) = 2; *((long* )&buf[11]) = len; 同じことをJAVAでしたいのですがわかりません。 (バイト型配列へint,long型を簡単に埋め込む方法がわかりません) よろしくお願いします。 よかったら、こちらもお願いします memcpy(&buf[15],message,strlen(messege)); TCPパケットのようなデータ処理が頻繁にある場合、 このようなコードは、どうしたらいいでしょうか? *((unsigned char* )&buf[15]) = message; CとJAVAになってますが、このような処理を簡単に書けないでしょうか?

    • ベストアンサー
    • Java
  • C言語のキャストについて

    お世話になります。 CRC-16の計算プログラムをC言語でつくりました。 例えば・・・1F 08 00 00 12 34 なら“1F0800001234”と入力すると【EEC2】と表示するプログラムです。 ただ・・・.Net SDKでコンパイルするとできたのですが、Visual C++2008でコンパイルするとエラーが出てしまいます。 (48) : error C2664: 'strlen' : 1 番目の引数を 'unsigned char [256]' から 'const char *' に変換できません。(新しい機能 ; ヘルプを参照) 1> 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。 (52) : error C2664: 'strtol' : 1 番目の引数を 'unsigned char [3]' から 'const char *' に変換できません。(新しい機能 ; ヘルプを参照) 1> 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。 型変換が必要ってことまではわかったのですが・・・必要なのはわかって行き詰まり状態です。。。 どのようしたらよいのでしょうか?ご教授をよろしくお願いします。 ソースは以下の通りです。 #include "stdafx.h" #include <stdio.h> #include <string.h> #include <stdlib.h> unsigned short crc_cal(unsigned short lng, unsigned char *str) { unsigned short crc, i, j, t; t= 0x0000; crc = 0xffff; for (i = 0; i < lng ; i++) { crc ^= (unsigned short) str[i]; t = (unsigned short) str[i]; for (j = 1; j <= 8; j++) { if (crc & 1) { // carry bit on crc = crc >> 1; crc ^= 0xa001; } else { // carry bit off crc = crc >> 1; } } } return crc; } int main(void) { unsigned char str[256],data[128],hexstr[3]; unsigned short crc,CRC,len; while(1) { printf("Please input key (HEX)\n"); scanf("%255s",str); hexstr[2]='\0'; for(len=0; len<(strlen(str)/2) ;len++) { hexstr[0]=str[len*2]; hexstr[1]=str[len*2+1]; data[len]=(unsigned char)strtol(hexstr, NULL, 16); } crc = crc_cal(len,data); CRC = (crc>>8) | (crc<<8); printf("\nCRC16 = %04X\n\n", CRC); } return 0; }

  • 配列へのプラス?

    C++のコードを読んでいるところなのですが、 配列に プラスされているコード部の意味がわからずこまっております。 ********************************* unsigned char data1[32]; unsigned char data2[8]; ※ここでdata1, data2に値入力処理 if(memcmp(data1 + 2, data2, 8) != 0){  //処理X } ********************************* 上記のようなコードの「data1 + 2」の部分がよくわかりません。 byte配列にプラスされている2は、数値なのでしょうか?? C++は普段使わないもので、、、呆れるほどアホな質問だとしてもご了承ください。。 どなたかご教授ねがいます。 よろしくお願いいたします。

  • シリアル通信チェックコードについて

    ある装置と自動化したいので、通信アナライザーを接続し、 通信ログをとりました。 しかし電文内に含まれている、チェックコード(チェックサム)の計算方法が どうしてもわかりません。 LRC、CRC8など試してみたのですが、 一致しないので、途方にくれております。 どなたかご教授いただけないでしょうか? 一番短い電文で以下となります。 -------- $(0x24) A(0x41) 0(0x30) '(0x60) <-CheckSum CR -------- ちなみに、装置には通信仕様書など一切の資料が無いため、 現状接続しているコントローラーにアナライザーを設置してログしています。

  • unsigned *という宣言について

    char *str = "\x01\x23\x45\x67\x89\xab\xcd\xef"; unsigned *u = (unsigned *)(str + 1); このようなコードをみかけたのですが、 unsigned *uという宣言が理解できません。 これはどのような型として解釈されているのでしょうか? 暗黙でunsigned int* uと解釈されるのでしょうか?

  • 配列から構造体へデータコピー

    配列から構造体へデータのコピーをしたいのですが、 構造体のメンバがビットフィールドで構成されている時の処理がわかりません。 --------test.c----------- #include <stdio.h> #include <string.h> typedef struct{ unsigned char aaa :1; unsigned char bbb :1; unsigned char ccc :1; unsigned int ddd :13; unsigned char eee :2; unsigned char fff :2; unsigned char ggg :4; }test_t; int main(void) { test_t test_t; unsigned char data[]={0x5F, 0xFE, 0x1C}; memcpy(&test_t, data, 4); printf("aaa = %X\n", test_t.aaa); printf("bbb = %X\n", test_t.bbb); printf("ccc = %X\n", test_t.ccc); printf("ddd = %d\n", test_t.ddd); printf("eee = %X\n", test_t.eee); printf("fff = %X\n", test_t.fff); printf("ggg = %X\n", test_t.ggg); return 0; } ------期待出力--------- aaa = 0 bbb = 1 ccc = 0 ddd = 1FFE eee = 0 fff = 1 ggg = 12 「test.c」を実行した時に「期待出力」のような出力を期待していたのですが、実際には aaa = 1 bbb = 1 ccc = 1 ddd = 1 eee = 0 fff = 0 ggg = 0 と表示されてしまいます。 ビットフィールドで構成された構造体に、配列の値をそのままあてる事は出来ないのでしょうか? 出来るだけ、マスクやシフト演算を使用しないで、配列からビット単位で値を抽出したいのですが・・・

  • CRCの計算方法について

    色々なサイトを参考にして、自分なりにCRC-ITU-TでCRCを計算する関数を作成しました。 いまいち理解が浅く、そのCRCの値が正しいのか判断できずに困っています。 以下にソースを載せます。 アドバイスを、どうかよろしくお願いします。 unsigned short Crc(unsigned char *Data, unsigned long num) {   unsigned short vCrc;    //CRCを計算する変数   unsigned char vData;   unsigned long i;   int j;   vCrc = 0;   vData = 0;   //初期化   for(i = 0; i <= num; i++){     vData = *(Data+i);   //1byte読み込み     for(j = 0; j < 8; j++){       //CRC計算変数がシフトで桁あふれする場合       if((vCrc & 0x8000) != 0){         vCrc = vCrc << 1;   //1bitシフト         vCrc = vCrc ^ 0x1021;  //多項式とXOR       }       else{         vCrc = vCrc << 1;       }       if((vData & 0x80) != 0){         vData = vData << 1;   //データ変数1bitシフト                 vCrc = vCrc ^ 0x0001;  //CRC計算変数に1をXOR       }       else{         vData = vData << 1;       }     }   }   return(vCrc); }

  • int型(2バイト)データの分割

    int型(2バイト)データの分割 マイコンのフラッシュ領域にint型のデータ(2バイト)を 1バイトづつ書き込むのに以下のコードを考えています。 unsigned int data; unsigned char dat1,dat2; data = 555; dat1 = 0; dat2 = 0; dat1 = (char)data; dat2 = (char)data >> 8; 以下、dat1とdat2をフラッシュに書き込む。 未熟者でプログラムとして合っているのか解りません。 やり方として問題ないでしょうか? またもっと良い方法があればご教授願います。

  • PWM を使ったSine波の生成について

    今、以前に投降した正弦波発振器とは別に、PIC16F88のPWMを使って正弦波を発生する小型の回路を作ろうと考えています。 以下のようなプログラムを作りました。 コンパイルは通るのですが、「error: (1250) could not find space (128 bytes) for variable F994」と「could not find space (128 bytes) for variable _data」というエラーメッセージがでてビルドができません。 どの個所が悪いのでしょうか教えてください。 また、開発環境については、MPLAB X IDE(バージョン3.20)でコンパイラーはXC8(バージョン3.60)を使用しています。 ///////////////////////////以下プログラム///////////////////////////////////////// #include <pic.h> #define _XTAL_FREQ 4000000 //CONFIGは省略 void ioport(void); void setCcpMode(unsigned char); void setPwmPeriod(unsigned char); void setPwmDuty(unsigned int); void ConfigTMR2(unsigned char set); void main(void) { unsigned int cnt; unsigned char data[128]={128,134,140,146,152,159,165,171,176,182,188,193,199,204,209,213,218,222,226,230,234,237,240,243,246,248,250,252,253,254,255,255,256,255,255,254,253,252,250,248,246,243,240,237,234,230,226,222,218,213,209,204,199,193,188,182,176,171,165,159,152,146,140,134,128,121,115,109,103,96,90,84,79,73,67,62,56,51,46,42,37,33,29,25,21,18,15,12,9,7,5,3,2,1,0,0,0,0,0,1,2,3,5,7,9,12,15,18,21,25,29,33,37,42,46,51,56,62,67,73,79,84,90,96,103,109,115,121}; ioport(); setCcpMode(CCP_PWM_ACTIVE_HIGH);//CCP1CONに0b00001100をセット setPwmPeriod(255);//PR2に255をセット setPwmDuty(0); ConfigTMR2(TMR2_DIV4_ON);//TMR2のプリスケーラーを4倍に、そしてTMR2をON while(1){ //duty=adconv(0); for(cnt=0;cnt<128;cnt++){ setPwmDuty(data[cnt]); } } } void ioport(void){ OSCCON=0x60; CMCON=0x07; TRISA=0x00; ANSEL=0x00; PORTA=0x00; TRISB=0x00; PORTB=0x00; } void setCcpMode(unsigned char mode) { CCP1CON= mode; } void setPwmPeriod(unsigned char T) { PR2=T; } void setPwmDuty(unsigned int duty) { unsigned char tmp; tmp=(unsigned char)((duty & 3)<<4); CCP1CON |=tmp; tmp=(unsigned char)(duty>>2); CCPR1L=tmp; } void ConfigTMR2(unsigned char set) { T2CON=set; }

専門家に質問してみよう