はじめてのModbus (1) 電力量モニタKM-N1-FLK
もうすぐUSB4.0という規格が決まるそうです。そうすると、マウスから8Kディスプレイの接続まで、周辺機器は全部USBのシリアル通信で統一される時代がやってきます。
IBM/PCが世の中に出たとき、シリアル通信はRS-232Cでした。プリンタはセントロニクスという8ビット・パラレル、拡張カード類はISAというパラレル・バス、ハードディスクもIDEというパラレル・バスでした。
RS-232Cは最終的にはEIA-232Dという正式な規格になりました。RS-232Cと呼ばれた時代が長かったので、非同期シリアル通信規格は現在UARTという呼び名が使われますが、RS-232Cという刷り込みがあります。最大±15V振幅で距離10mの通信を行います。
よく似た規格にRS-422があります。これはAppleのMacintoshがAppleTalk用の物理層で使っていました。もう一つRS-485があります。
- 差動で信号を伝送するので、ノイズ耐性が上がり1,200m以上まで装置間を伸ばせる
- 振幅を抑えることでビット・レートが上がり、最大10Mbps
- 装置は最大32+32の64個までぶら下げられるマルチドロップ
これらの特徴をもつため、ビル内、監視カメラの制御、工場内のデータ通信などに使われています。
RS-485は物理層なので、実際の通信を行うには通信プロトコルが規定されていないと、機器間はデータのやり取りをできません。こちらの記事でも、さまざまな事例を使いました。ここでは、工場などで一般的なModbusを使います。Modbusには次の3種類があります。
- Modbus/ASCII データはASCIIコード。あまり使われていない
- Modbus/RTU RTU(Remote Terminal Unit)のデータは16進値。
- Modbus/TCP RS-485ではなくイーサネットにModbus/RTUのコードを載せる
Arduino UNOの時代、純正のRS-485インターフェースはありませんでした。IoTを標榜するMKRシリーズの製品が増えたころ、ここで用いるArduino MKR 485 Shieldが出荷を始めました。入出力はノイズの影響を少なくするために絶縁されていますが、実験ではそうでない機器でもつないで使えます。
ライブラリは半年ほど遅れ、2018年夏に出ました。
次の写真は、MKRZEROの上にArduino MKR 485 Shieldを挿しこんで利用している様子です。このシールドの左の端子は外部電源をつなげられます。右の緑色の端子に出ている電源はそれらとは絶縁されています。
●USB-RS485ボードをPCに挿す
Windows10が物理レイヤのRS-485をアクセスできるように、USB-RS485アダプタを用意します。
入手したボードではUSB-シリアル変換にCP210xやCH34xが使われていました。Windows10では自動でドライバが入ると思います。アマゾンやebayでは千円前後で入手できます。
●接続するのはオムロンの小型電力量モニタKM-N1-FLK
KM-N1-FLKは、最大で四つの交流電流センサCTをつなげられるコンパクトな形状をした電力計です。RS-485でアクセスできます。電流センサCTは5A/50Aの最小容量KM-NCT-5A/50Aを、接続ケーブル KM-NCB-1Mをアマゾンで入手しました。測定を兼ねているAC100Vをつなげることで、別途電源は不要です。
デフォルトの通信プロトコルはオムロン独自のCompoWay/Fです。パネルの操作でも変更できますが、PCのユーティリティKM-NSettingToolを用いてModbus/RTUに変更する手順は、こちらの記事を参照してください。通信条件は、8ビット、パリティなし、ストップ・ビット2です。このKM-N1-FLKでは、8ビット、パリティなし、ストップ・ビット1は設定できません。
●接続
Arduino MKR 485 ShieldとKM-N1-FLK を2本のケーブルで接続します。結線は次のようにします。
Arduino MKR 485 Shield上のDIPスイッチは全部OFFで動きます。RS-485の終端抵抗は両端に入れるのが正しいので、Y-ZのターミネイトをONにします。
●スケッチ
ライブラリはArduinoModbusとArduino RS485の二つをインストールします。サンプルのModbusRTUTemperatureSensorを修正します。8ビット、ストップ・ビット2、パリティなしに設定したので、初期化のところにその指定を入れます。
if (!ModbusRTUClient.begin(9600,SERIAL_8N2)) {
計測データはHOLDING_REGISTERSを読み出します。電圧は0x0000、電流は0x0006を読み出すと得られます。
if (!ModbusRTUClient.requestFrom(1, HOLDING_REGISTERS, 0x0006, 2)) {
この2か所を修正してコンパイル、実行します。AC100V側の電圧97V前後が読み出せました。
上記のスケッチをベースに電力計の電圧を読み出すスケッチを作りました。レジスタ0x0000を読み出した電圧のフォーマットは、データシートには次のように書かれています。
H'00000000~H'0098967F(0~9999999) 電圧の10倍値 |
ArduinoModbusライブラリのリードは16ビット単位と思われるので、二度読み出し、32ビットの値を10で割ります。
// created 8 August 2018 by Riccardo Rizzo
#include <ArduinoModbus.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("Modbus KM-N1-FLK AC Voltage");
// start the Modbus RTU client
if (!ModbusRTUClient.begin(9600, SERIAL_8N2)) {
Serial.println("Failed to start Modbus RTU Client!");
while (1);
}
}
void loop() {
// send a Holding registers read request to (slave) id 1, for 2 registers
if (!ModbusRTUClient.requestFrom(1, HOLDING_REGISTERS, 0x0000, 2)) {
Serial.print("failed to read registers! ");
Serial.println(ModbusRTUClient.lastError());
} else {
int32_t ACV = ModbusRTUClient.read();
ACV = ACV <<16 | ModbusRTUClient.read();
Serial.println(ACV / 10.0);
}
delay(2000);
}
実行中の様子です。
コラム 波形
PicoScopeでrequestFrom()の波形を観測しました。