PowerLogic EthernetGateway(EGX100) による H663 データ取得早分かり手引き 2011 年 11 月 11 日 JAVASYS
1. 概要 H663 は RS-485 によって上位機と通信し データのやりとりを行います 本仕様書は PowerLogic EthernetGateway(EGX100) によるデータ取得の開発に関して簡単な手引きを記述します EGX100 と H663 は RS-485 で通信しますが 本システムにおいて PC またはサーバは EGX100 とイーサネット通信を行います なお 詳細については各仕様書を参照してください 機器構成 EGX100 LAN PC またはサーバ RS-485 H663 H663
2.MODBUS PC またはサーバは MODBUS 手順によって H663 とのやりとりを行います MODBUS についての詳細は下記仕様書を参照してください Modbus_Application_Protocol_V1_1b.pdf Modbus_Messaging_Implementation_Guide_V1_0b.pdf Modbus_over_serial_line_V1_02.pdf Object_Messaging_Protocol_ExtensionsVers1.1.doc また H663 の MODBUS 関連情報は h663&704 modbus pomap.pdf に記述してあります 本書では MODBUS で H663 より CH#1 データの取得について記述します 一般的な MODBUS のデータフォーマットは下記のとおりです 1 バイト 1 バイト可変 2 バイト スレーブアドレス ファンクションデータ部エラーチェックコード ここで スレーブアドレスは RS-485 においてこの H663 を特定するためのアドレスです ファンクションは H663 に対するコマンドです 読出しの場合 03H になります データ部は 上記ファンクション ( コマンド ) に付随するデータです エラーチェックコードは 通信上データエラーをチェックするためのコードになります なお エラーチェックコードとして CRC-16 が用いられます CRC-16 については例えば 以下の URL に記述があります http://ja.wikipedia.org/wiki/%e5%b7%a1%e5%9b%9e%e5%86%97%e9%95%b7%e6%a4% 9C%E6%9F%BB 読出しファンクションに対する H663 の応答は下記の通りです 1バイト 1バイト 1バイト nバイト 2バイト 01H ファンクション 03H データ長 n データ部 xxh..xxh エラーチェックコード データ長 n は次に続くデータ部のデータ長を表します バイト数を示します 本システムは シュナイダー ( 富士電機 ) の EGX100 経由で H663 データの取得を行うためプロトコルは MODBUS/TCP(PC またはサーバからイーサ経由でアクセス ) を使用します 従って データフォーマットは以下のようになります 2バイト 2バイト 2バイト可変 転送 ID フ ロトコル ID 転送ハ イト数チェックデータ抜きで MODBUS データ
従って 下記のようになります 2バイト 2バイト 2バイト可変転送 ID フ ロトコル ID 転送ハ イト数チェックデータ抜きで MODBUS データ 1 バイト 1 バイト可変 スレーブアドレス ファンクション データ部 ここで 転送 ID=00H 00H 固定プロトコル ID=00H 00H 固定転送バイト数 = スレーブアドレス以下の転送総バイト数です MODBUS/TCP の場合 TCP/IP においてエラーフリーになったためエラーチェックコードが不要となります
3. 具体例 :H663 の CH#1 のデータ取得 H663 の CH#1 の取得にあたって データ読出し のファンクションコードを用いますが 03H ( ヘキサで 03) がそれにあたります また 03H のファンクションコードに続き データ部 として読出しするアドレス (Reg#) および読み出しワード数 ( それぞれ 2 バイト ) を指定する必要があります 例えば CH#1 の場合 Reg#1 になります 従って CH#1 の読み出しについてはファンクション =03H データ部 =00H 00H 00H 01H ( 読出しアドレス =0000H(Reg#=1) 読み出しワード数 =0001H) 以上の読み出しについて H663 に送出するデータは下記のようになります ( スレーブアドレス =01H とします ) 2バイト 2バイト 2バイト 転送 ID フ ロトコル ID 転送ハ イト数 00H 00 H 00H 00H 00H 06H 1バイト 1バイト 4バイト スレーフ アト レス ファンクション 開始アト レスワード数 01H 03H 00H 00H 00H 01H 注 ) データ部において ワードデータ (16 ビット ) の情報を扱っていますが 送信時 データ部は HighByte LowByte の順 送信します 上記のコマンドで CH#1 のデータを取得することになりますが H663 の返信は下記の通りです 2 バイト 2 バイト 2 バイト 1 バイト 1 バイト 1 バイト 2 バイト 転送 ID フ ロトコル ID 転送ハ イト数 00H 00H 00H 00H 00H 05H スレーフ アト レス 01H ファンクション 03H データ長 n 02H 07H 88H 上記のデータ例は CH#1 データが 0788H の場合を示します
4. 具体例 : H663 の CH#1 のデータ取得 のサンプルプログラム H663 の CH#1 取得に関するサンプルプログラムを用意しました 本プログラムは プログラム名として H663Sample です Linux 上で動作し 検証は Cent-OS(RedHat 系 ) 上で行いました 以下に H663sample を実行した時のやりとりログを示します H663Sample の起動は次の通りです./H663Sample 192.168.1.50 502 1 上記の例では EGX100 の IP アドレス =192.168.1.79 ポート番号 =502 で H663 のスレーブアドレス =1 とします [yamagishi@svr1 cprogram]$./h663sample 192.168.1.79 502 1 2011/11/11 18:19:17[5250]:H663Sample Start 2011/11/11 18:19:17[5250]:--->00 00 00 00 00 06 01 03 2011/11/11 18:19:17[5250]:--->00 00 00 01 2011/11/11 18:19:17[5250]:SendData[12]bytes 2011/11/11 18:19:17[5250]:Connect OK 2011/11/11 18:19:17[5250]:Recv Start 2011/11/11 18:19:17[5250]:Recv End Ret[11] 2011/11/11 18:19:17[5250]:<--- 2011/11/11 18:19:17[5250]:00 00 00 00 00 05 01 03 02 07 88 2011/11/11 18:19:17[5250]:H663Sample End CH#1 の値 備考 ) EGX100 の IP アアドレスは EGX100 の設定によって決まります ポート番号は基本的に 502 で固定になります スレーブアドレスは EGX100 に接続した H663 のスレーブアドレスです スレーブアドレスは H662 基板上の DIP スイッチによって設定できます ( 詳細については H663 Installation Guide を参照してください ) 上記の例において 取得した H663 の CH#1 の値は 07 88H です (10 進数の値 =1928)
5. 具体例 : H663 の CH#1 のデータ取得 のサンプルプログラムのソース 本プログラムは Linux 上で動作するように C 言語で作成でしました コンパイルは gcc によって行います コンパイル操作は次の通りです gcc H663Sample.c o H663Sample なお 本プログラムはサンプルという性質から分かりやすく見て頂くために詳細なエラー処理を省略してあります ソース内容は下記のとおりです /*---------------------------------------------------- H663Sample.c: Send command to H663 to get CH#1 data. */ #include <stdio.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <time.h> #include <netdb.h> #include <linux/if.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <netinet/in.h> #include <limits.h> char DoPrAndPutLOG(unsigned char *LogData); DoPutLOG_2(unsigned char *LogData); g_logmes[512]; /*---------------------------------------- Make Words message(command) to send to EGX100(H663) to get CH# data. Exit: Number of SendData */ DoMakeCommand(unsigned char *Dat, char SlaveNo ) { Len; Dat[0] = 0x00; Dat[1] = 0x00; Dat[2] = 0x00; Dat[3] = 0x00; /* Number Transfer bytes */ Dat[4] = 0x00; /* High */ Dat[5] = 0x06; /* Low */ /* Slave Addres */ Dat[6] = SlaveNo; /* Function 03H = Read */ コマンド文字列の準備 ( 次ページに続く )
Dat[7] = 0x03; /* Start Reg Address */ Dat[8] = 0x00; /* High */ Dat[9] = 0x00; /* Low */ /* Number of words to read */ Dat[10] = 0x00; /* High */ Dat[11] = 0x01; /* Low */ Len = 12; コマンド文字列の準備 sprf(g_logmes,"--->%02x %02x %02x %02x %02x %02x %02x %02x", Dat[0],Dat[1],Dat[2],Dat[3],Dat[4],Dat[5],Dat[6],Dat[7]); DoPrAndPutLOG(g_LogMes); sprf(g_logmes,"--->%02x %02x %02x %02x", Dat[8],Dat[9],Dat[10],Dat[11]); DoPrAndPutLOG(g_LogMes); return Len; /*---------------------------------------- Main proc. */ main(argc, argv) argc; char *argv[]; { unsigned char ssnd[256]; unsigned char srcv[256]; char SlaveNo; fd,tcp_port,i; struct sockaddr_in dest; FILE *fh; SockOptflag; Ret; Len; struct timeval tv; ChrCnt,FirstTime; char swork[9]; DoPrAndPutLOG("H663Sample Start"); if ( argc < 4 ){ sprf(g_logmes,"usage: H663Sample [IPAdr][PortNo][H663SlaveAdr]"); DoPrAndPutLOG(g_LogMes); return 0; SlaveNo = argv[3][0] & 0x0f; bzero(ssnd,sizeof(ssnd)); Len = DoMakeCommand( ssnd, SlaveNo ); sprf(g_logmes,"senddata[%02d]bytes",len); DoPrAndPutLOG(g_LogMes); /* Create a socket */ fd = socket(af_inet, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) { DoPrAndPutLOG("Socket failed"); return 0; bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; tcp_port = atoi(argv[2]); /* get server port */ dest.sin_port = htons(tcp_port); inet_aton(argv[1], &dest.sin_addr); /* get server ip */ /* Connect to Equiptment, retry 3 times*/ for(i=0; i<3; i++) { Ret = connect(fd, (struct sockaddr*)&dest, sizeof(dest)); if (Ret == 0) { DoPrAndPutLOG("Connect OK"); break; if (i == 3) { DoPrAndPutLOG("Connect failed"); close(fd); return 0; /* Send Command to EGX100(H663) */ send(fd, ssnd, Len, 0); /*when data received, send them back*/ DoPrAndPutLOG("Recv Start"); bzero(srcv,sizeof(srcv)); Ret = recv(fd, srcv, 128, 0); sprf(g_logmes,"recv End Ret[%d]",Ret); DoPrAndPutLOG(g_LogMes); if (Ret < 1) Ret = 32; DoPrAndPutLOG("<---"); ChrCnt = 0; g_logmes[0] = ' 0'; for(i=0; i<ret; i++) { sprf(swork,"%02x ",srcv[i]); strcat(g_logmes,swork); ++ChrCnt; if(chrcnt >= 16){ DoPrAndPutLOG(g_LogMes); g_logmes[0] = ' 0'; ChrCnt = 0; if ( ChrCnt > 0 ){ DoPrAndPutLOG(g_LogMes); close(fd);
DoPrAndPutLOG("H663Sample End"); return 0; /*---------------------------------------- Display String to Console also save it to log file. */ DoPrAndPutLOG(unsigned char *LogData) { time_t timer; struct tm* t_st; pid_t pid; unsigned char snowstring[32]; unsigned char slogstring[512]; pid=getpid(); time(&timer); t_st = localtime(&timer); time(&timer); t_st = localtime(&timer); sprf(snowstring,"%04d/%02d/%02d %02d:%02d:%02d", t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday, t_st->tm_hour,t_st->tm_min,t_st->tm_sec); sprf(slogstring,"%s[%d]:%s n",snowstring,pid,logdata); prf(slogstring); return(doputlog_2(slogstring)); /*---------------------------------------- Save String Message to log file. */ DoPutLOG_2(unsigned char *LogData) { FILE *fp; unsigned char sfullpath[256]; strcpy(sfullpath,"h663sample.log"); /* Open File */ if ((fp=fopen((const char *)sfullpath,"a"))==null){ return 1; /* Put File */ fputs((const char *)LogData,fp); /* File Close */ fclose(fp); return 0;
付録 A. 工場出荷時の設定 EGX100 IP:169.254.0.10 ID:Administrator PASS:Gateway なお 本システムのテストに当たって EGX100 の IP アドレスを 1 系統に変更しました 192.168.1.79 H663 RS485-2Wire ボーレート 9600 パリティなしスレーブアドレス =1 以上 DIP スイッチの設定です