1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

サーバやPC上のプログラムでModbus機器からデータを取得 (1)

Posted at

サーバやPC上のプログラムでModbus機器からデータを取得、なんて場面があります。例えば、接点やセンサーがつながったリモートIOからデータを取得したい場合などです。

この記事では、Modbus/TCPでリモートIOなどからデータを取得するサンプル・プログラムを紹介します。

この記事で紹介するサンプルはModbus/TCPを前提としていますが、Modbus/RTUでも原理は同じです
。Socketがシリアル・インタフェースに変わり、やりとりするデータのフォーマットがTCPからRTUに変わるだけです。
※RTUの場合、送受するデータにCRCを組み込む必要があり、少々やっかいですけどね。

リモートIOのメーカーのサイトでは、PLCがリモートIOからデータを取得するように書かれていたりします。リモートIOからのデータを使って機械を制御する場合はそんな構成なのかもしれませんが、そのデータをサーバやPCで処理したい場合には、リモートIOのデータをサーバやPCが直接取得するのが合理的です。リモートIOとPLCは必ずしもセットではないのです。
「そんなことは、ここであらためて書かなくてもあたりまえだろ」と思われる方も多いでしょうが、固定観念と先入観が、迷わず(本来は不要であるはずの)PLCを登場させてしまいます。

Modbus/TCP

Modbusについては、エム・システム技研さまのこのドキュメントがおすすめです。
Modbus プロトコル概説書

サンプル・プログラム

今回はPerlを使いました。Perlである必要性はありませんが、Perlではだめだという理由もありません。なぜPerlを選択したか?僕のセルフォンでPerlが使えたからです。

サンプルでは、ファンクション0x03: Read Holding Register を使っています。

データをコンソールに16進表記で表示するために、公開されていたコレを遠慮なく拝借いたしました。
(Perl) バイナリ変数の16進ダンプ表示

#!/usr/bin/perl

use IO::Socket;

{
	my $Server = 'localhost';
	my $Port = 5502;

	my $socket = new IO::Socket::INET( 
		PeerAddr=>$Server,
		PeerPort=>$Port,
		Proto=>'tcp');
	die "IO::Socket : $!" unless $socket;
	print "connected to the server\n";

	my $TransactionID = 0x0200;
	my $ProtocolID = 0x0000;
	my $Length = 0x0006;

	my $UnitID = 0x01;
	my $FunctionCode = 0x03;
	my $StartAddress = 0x0000;
	my $CountRegister = 0x0001;

	my $req = pack("n3C2n2", 
		$TransactionID, $ProtocolID, $Length, 
		$UnitID, $FunctionCode, 
		$StartAddress, $CountRegister);
	my $size = $socket->send($req);
	print "sent data:\n";
	BinaryDump($req);

	shutdown($socket, 1);

	my $response = "";
	$socket->recv($response, 1024);
	print "received response:\n";

	BinaryDump($response);

	$socket->close();
}

# 利用させていただきました
# https://netlog.jpn.org/r271-635/2018/11/perl-bynary-dumper.html
sub BinaryDump {
    my ($buf) = @_;
    my $len;
    my $i;
 
    $len = length($buf);
    printf ("length = %d\n", $len);
    for ($i = 0; $i < $len; $i++) {
        printf("%02X ", ord(substr($buf, $i, 1)));
        # 16文字目で画面上の改行
        if (($i % 16) == 15) {
            print "\n";
        }
    }
    if (($i % 16) != 15) {
        print "\n";
    }
}

テスト

テストにも僕のセルフォンを使いました。
Modbusスレーブとして、Androidアプリケーション"ModTCPSimX"に活躍いただきました。
672850389bd205ccd2287edea4b6eea462df857ac0e06.png
ModTCPSimX のポート番号のデフォルトは5502です。僕のプログラム側も5502にしてあります。
※一般的なModbus/TCPは502です。
Holding Register 40001 に任意の数値を設定しておきます。
※ModTCPSimX では値の背景が黄色の場合は更新がコミットされていません。鉛筆アイコンでコミットするのを忘れずに
e0e934f699c14d7e14657cced9848d5c62df8d8718344.png
Termuxでプログラムを動かすと、最後のバイトに値が置かれたTXが返ります。
a486c6b8599b14f88005f174460cdbd662df8dad794c7.png

次回

せっかくなので、次回は取得したデータをデータベースに保存します。次回は今回以上にどうってことない記事です。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?