目次
前
次
ArduinoMega2560Modbus処理
ArduinoMega2560は、シリアルインタフェースを
0から3の4チャネル持ちます。
チャネル2を、RS485上の通信プロトコルであるModbusに
よる処理で、測定機器と情報交換してみました。
通信相手は、電力計モニタのKM50C。
Modbusを使うために、ArduinoMega2560はマスターで
KM50Cはスレーブとします。
通信プロトコルの諸元は、次のように指定。
データ転送速度 38.4kbps
データ長 8ビット
パリティ EVEN
ストップビット 1ビット
デフォルトでは、データ転送速度は9600bpsですが
最高の38.4kbpsにし、ArduinoMega2560が反応可能
かを調べることを目標にしました。
Arduinoには、Modbusマスター用ライブラリが
存在するので、使わせて貰います。
ArduinoIDEから、ModbusMasterライブラリをダウン
ロードすると、使えるようになりました。
ModbusMasterライブラリ関係ソースコードを、GitHubから
ダウンロードして解析。
ディレクトリsrcにC++のソースコードが含まれています。
Modbusはシーケンサ(Programmable Logic Circuit)で利用
することを想定しているので、相手の計測制御機器のIDと
内蔵してるレジスタへの書込み、読込みをフレームで処理
しています。
フレームの構成は、以下のようになってます。
コマンドフレーム構成
レスポンスフレーム構成
どちらのフレームでも、CRCの計算が必要になりますが
ModbusMasterライブラリで対応するので、使うだけなら
気にしないで済みます。
RS485による通信は、平衡かつ半二重になるのでICを使い
ハードウエア上の面倒な処理を肩代わりして貰います。
今回は、秋月電子で入手できるインタフェース基板を利用。
6ピンをArduinoMega2560に接続します。
6ピンの内容は、以下。
Vccは、5V、3.3Vのどちらでもよく
TxD、RxDはチャネル2に接続。
nRE、DEは受信と送信を入れ替えるときに利用
するので、通常GPIOで使っているピンに接続。
nRE、TEの扱い方は、使っているICのブロック図を
見ると、動作を理解できます。
平衡伝送のため、次の様にAとY、BとZをショートし
終端抵抗を接続して使います。
ハードウエアの用意ができたなら、ModbusMasterライブラリを
Arduinoスケッチで、どう利用するのかを見ていきます。
ライブラリ利用宣言とピンアサイン
Arduinoスケッチの先頭では、ライブラリの利用を
宣言すると同時に、RS485のICのピンアサイン指定。
#include <ModbusMaster.h>
/* Power Controller */
ModbusMaster node_power;
#define MPOWER_TDE 6
#define MPOWER_RDE 7
今回は、DEとnREをデジタル6、7ピンに接続します。
初期化
関数setupの中で、シリアルチャネルとModbusに関連する
指定をして、初期化します。
利用する関数として定義。
void init_power()
{
/* set control pin directions */
pinMode(MPOWER_TDE,OUTPUT);
pinMode(MPOWER_RDE,OUTPUT);
/* Main Controller */
Serial2.begin(38400,SERIAL_8E1);
/* Modbus slave ID 1 */
node_power.begin(1, Serial2);
/* Callbacks allow us to configure the RS485 transceiver correctly */
node_power.preTransmission(preTransmission);
node_power.postTransmission(postTransmission);
}
preTransmission、postTransmissionは、サイレント
インターバルを生成するために使います。
2関数は、次のように定義。
void preTransmission()
{
digitalWrite(MPOWER_TDE,HIGH);
digitalWrite(MPOWER_RDE,HIGH);
}
void postTransmission()
{
digitalWrite(MPOWER_TDE,LOW);
digitalWrite(MPOWER_RDE,LOW);
}
コマンド送信前に、RS485のICの送信部分をイネーブルにし
受信部分をディセーブルにします。
コマンド送信が終了したなら、送信部分をディセーブルにし
受信部分をイネーブルにします。
この2つの関数を利用することで、半二重を実現。
通信処理
コマンドは、スレーブIDと機能番号を連らねて
レジスタアドレスを並べた後、CRCで締めます。
ModbusMasterライブラリでは、機能番号を指定する関数は
決められていて、次のように対応。
0x03 readHoldingRegisters
0x04 readInputRegisters
また、電力計の内部レジスタアドレスはデータシートに
一覧表にされています。
電力計から計測値を取得する関数を定義。
byte get_power_ppm(unsigned long *ptr)
{
byte ii ;
byte jj ;
word dh ;
word dl ;
unsigned long tmp ;
byte flag ;
byte result ;
/* default */
flag = 0 ;
/* 0x03 voltage and current => 0x0000 - 0x0005 */
result = node_power.readHoldingRegisters(0,12);
if ( result == node_power.ku8MBSuccess ) {
for ( ii = 0 ; ii < 13 ; ii++ ) {
/* calculate pointer */
jj = 2 * ii ;
/* get word */
dh = node_power.getResponseBuffer(jj);
dl = node_power.getResponseBuffer(jj+1);
/* concatenate */
tmp = dh * 65536 + dl ;
/* store */
*(ptr+ii) = tmp ;
}
} else {
flag |= (1 << 0) ;
}
delay(10);
/* 0x03 voltage and current => 0x0006 - 0x000B */
result = node_power.readHoldingRegisters(6,12);
if ( result == node_power.ku8MBSuccess ) {
for ( ii = 0 ; ii < 13 ; ii++ ) {
/* calculate pointer */
jj = 2 * ii ;
/* get word */
dh = node_power.getResponseBuffer(jj);
dl = node_power.getResponseBuffer(jj+1);
/* concatenate */
tmp = dh * 65536 + dl ;
/* store */
*(ptr+ii+6) = tmp ;
}
} else {
flag |= (1 < 1) ;
}
delay(10);
/* 0x03 voltage and current => 0x000C */
result = node_power.readHoldingRegisters(12,2);
if ( result == node_power.ku8MBSuccess ) {
/* get word */
dh = node_power.getResponseBuffer(0);
dl = node_power.getResponseBuffer(1);
/* concatenate */
tmp = dh * 65536 + dl ;
/* store */
*(ptr+12) = tmp ;
} else {
flag |= (1 << 2) ;
}
return flag ;
}
32ビットの整数値を配列の要素として保存する
仕様としました。配列は、次のように指定。
unsigned long ppmx[13];
コーリングシーケンスは、次のように単純。
/* get informations from METER */
xx = get_power_ppm( ppmx );
/* judge */
if ( xx ) {
rs_puts(" power getting is failed ");
crlf();
}
show_power_ppm( ppmx );
取得値を表示するために、次の関数を定義。
void show_power_ppm(unsigned long *ptr)
{
char msg[11];
char scode ;
byte ii ;
byte jj ;
unsigned long tmp ;
boolean sflag ;
/* loop */
for ( ii = 0 ; ii < 13 ; ii++ ) {
sflag = OFF ;
/* heading */
switch ( ii ) {
case 0 :
rs_puts("Voltage1 ");
break ;
case 1 :
rs_puts("Voltage2 ");
break ;
case 2 :
rs_puts("Voltage3 ");
break ;
case 3 :
rs_puts("Current1 ");
break ;
case 4 :
rs_puts("Current2 ");
break ;
case 5 :
rs_puts("Current3 ");
break ;
case 6 :
rs_puts("Factor ");
break ;
case 7 :
rs_puts("Frequency ");
break ;
case 8 :
case 9 :
rs_puts("Efficient ");
break ;
case 10 :
case 11 :
rs_puts("No Efficient ");
break ;
case 12 :
rs_puts("Total ");
break ;
default :
break ;
}
/* get value */
tmp = *(ptr+ii) ;
if ( tmp & 0x80000000 ) {
sflag = ON ;
tmp ^= 0xffffffff ;
tmp++ ;
}
/* seperate */
*(msg+10) = '\0' ;
for ( jj = 0 ; jj < 10 ; jj++ ) {
*(msg+9-jj) = get_asc( tmp % 10 );
tmp /= 10 ;
}
/* zero surpress */
if ( *(msg+0) == '0' ) {
*(msg+0) = ' ' ;
if ( *(msg+1) == '0' ) {
*(msg+1) = ' ' ;
if ( *(msg+2) == '0' ) {
*(msg+2) = ' ' ;
if ( *(msg+3) == '0' ) {
*(msg+3) = ' ' ;
if ( *(msg+4) == '0' ) {
*(msg+4) = ' ' ;
if ( *(msg+5) == '0' ) { *(msg+5) = ' ' ; }
}
}
}
}
}
/* show */
scode = ' ' ;
if ( sflag ) { scode = '-' ; }
rs_putchar( scode );
rs_puts( msg );
/* unit */
switch ( ii ) {
case 0 :
case 1 :
case 2 :
rs_puts(" x 0.1V");
break ;
case 3 :
case 4 :
case 5 :
rs_puts(" x 0.001A");
break ;
case 6 :
rs_puts(" x 0.01");
break ;
case 7 :
rs_puts(" x 0.1Hz");
break ;
case 8 :
rs_puts(" x 0.1 W");
break ;
case 9 :
rs_puts(" x 0.01kW");
break ;
case 10 :
rs_puts(" x 0.1 var");
break ;
case 11 :
rs_puts(" x 0.01 kvar");
break ;
case 12 :
rs_puts(" x 0.1 kWh");
break ;
default :
break ;
}
/* new line */
crlf();
}
}
実際に端末ソフトを使って、電力計から計測値を
入力すると、次のように表示します。
M get power from METER
M
Voltage1 2051 x 0.1V
Voltage2 2073 x 0.1V
Voltage3 2097 x 0.1V
Current1 2937 x 0.001A
Current2 2888 x 0.001A
Current3 2935 x 0.001A
Factor - 0096 x 0.01
Frequency 0500 x 0.1Hz
Efficient - 0232 x 0.1 W
Efficient - 0002 x 0.01kW
No Efficient 0063 x 0.1 var
No Efficient 0000 x 0.01 kvar
Total 0160 x 0.1 kWh
M
Voltage1 2050 x 0.1V
Voltage2 2073 x 0.1V
Voltage3 2095 x 0.1V
Current1 2935 x 0.001A
Current2 2887 x 0.001A
Current3 2932 x 0.001A
Factor - 0096 x 0.01
Frequency 0499 x 0.1Hz
Efficient - 0224 x 0.1 W
Efficient - 0002 x 0.01kW
No Efficient 0063 x 0.1 var
No Efficient 0000 x 0.01 kvar
Total 0160 x 0.1 kWh
項目と数字の間に「−」があると、その数値は
負の数になっていると読みます。
目次
前
次