Armadilloフォーラム

modbusアドレスの読み方

kumikoohashi

2018年8月31日 16時53分

お世話になっております。大橋です。
RS-485アドオンモジュールと、C言語のlibmodbusで、機器からデータの読み出しをしようとしています。
しかし、組み込みに不慣れで、初めて使う機器の仕様書のレジスタの読み方がわからず、libmodbusでどう書けばいいのかわからず困っています。

データの取得について、仕様にはこう書いてあります。アドレスは、連結する機器一台ずつに連番で設定しています。
アドレス:01h〜0FH
コマンド:04h

電力量の項目
入力レジスタ:30002
相対アドレス:0x0001
ワード数:2
バイト数:4

から

機器製造番号の項目
入力レジスタ:30030
相対アドレス:0x001D
ワード数:2
バイト数:4

「メーターへ送信する相対アドレスは入力レジスタから30001を引いた 16 進数 2 桁。」とあり、
「アドレス 05 のメーターから電力量(正方向)を読み出す」例文が以下として書かれています。

05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)

これをlibmodbusで書くと、どうなるのでしょうか。以下のようにあてはめていますが、うまく動きません。

ctx = modbus_new_rtu(略);

modbus_set_slave(ctx,0x05);…アドレス
modbus_connect(ctx);
modbus_read_input_registers(ctx,【?0x0001?】,【?4?】,&re_reg);…コマンド04,レジスタ、バイト数

レジスタのアドレスの指定を0x0001や0x00D1とすると、値はかえってくるのですが、おかしな値です。
30002だとエラーになります。

初歩的ですが、Armadilloのサイトにあるものや他のサイトも調べましたがわからなかったので、どなたか教えていただけないでしょうか。

コメント

at_koseki

2018年8月31日 17時43分

古関です。

ご利用の対向機のデータシートを見ないと何とも言えない部分があるのですが、
対向機側のスレーブアドレス設定があっている前提にお話しします。

modbus_read_input_registers()の第3引数はバイトじゃなくてワード指定ではないでしょうか。
http://libmodbus.org/docs/v3.1.1/modbus_read_input_registers.html

とすると、以下のような感じでしょうか。

unsigned short re_reg[2];
・・・
modbus_read_input_registers(ctx, 0x0001, 2, re_reg);

すでにご確認済みかもしれませんが、以下modbusのhowtoも参考になるかもしれません。
https://armadillo.atmark-techno.com/howto/armadillo-400-modbus
https://armadillo.atmark-techno.com/armadillo-iot-g3l-modbus

kumikoohashi

2018年9月3日 2時18分

古関様

お世話になります。

> 対向機側のスレーブアドレス設定があっている前提にお話しします。
仕様書の、アドレス:01h〜0FH これをスレーブ番号として良いんですよね。
例文"05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)"だと、頭の05のところです。
仕様書に「スレーブ」という用語がないので、戸惑っています。

>
> modbus_read_input_registers()の第3引数はバイトじゃなくてワード指定ではないでしょうか。
ご指摘ありがとうございます。
そう修正しました。

よろしくお願いいたします。

koshiba

2018年8月31日 17時49分

> お世話になっております。大橋です。
> RS-485アドオンモジュールと、C言語のlibmodbusで、機器からデータの読み出しをしようとしています。
> しかし、組み込みに不慣れで、初めて使う機器の仕様書のレジスタの読み方がわからず、libmodbusでどう書けばいいのかわからず困っています。
>
> データの取得について、仕様にはこう書いてあります。アドレスは、連結する機器一台ずつに連番で設定しています。
> アドレス:01h〜0FH
> コマンド:04h
>
> 電力量の項目
> 入力レジスタ:30002
> 相対アドレス:0x0001
> ワード数:2
> バイト数:4
>
> から
>
> 機器製造番号の項目
> 入力レジスタ:30030
> 相対アドレス:0x001D
> ワード数:2
> バイト数:4
>
> 「メーターへ送信する相対アドレスは入力レジスタから30001を引いた 16 進数 2 桁。」とあり、
> 「アドレス 05 のメーターから電力量(正方向)を読み出す」例文が以下として書かれています。
>
> 05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)
>
> これをlibmodbusで書くと、どうなるのでしょうか。以下のようにあてはめていますが、うまく動きません。
>
> ctx = modbus_new_rtu(略);
> 略
> modbus_set_slave(ctx,0x05);…アドレス
> modbus_connect(ctx);
> modbus_read_input_registers(ctx,【?0x0001?】,【?4?】,&re_reg);…コマンド04,レジスタ、バイト数
>
> レジスタのアドレスの指定を0x0001や0x00D1とすると、値はかえってくるのですが、おかしな値です。
> 30002だとエラーになります。
>
> 初歩的ですが、Armadilloのサイトにあるものや他のサイトも調べましたがわからなかったので、どなたか教えていただけないでしょうか。

y.nakamura

2018年8月31日 17時54分

中村です。

> データの取得について、仕様にはこう書いてあります。アドレスは、連結する機器一台ずつに連番で設定しています。
> アドレス:01h〜0FH
> コマンド:04h

ここにある04hは、modbus_read_input_registers()が
内部的にこの値04hを入れてくれますので、
呼び出し側は気にする必要はありません。
(MODBUS仕様書にある Read Input Register の
Function Code の"04"のことならば...ですが)

> 電力量の項目
> 入力レジスタ:30002
> 相対アドレス:0x0001
> ワード数:2
> バイト数:4

> 05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)

この部分をコードにすると、
uint16_t re_reg[2];
modbus_read_input_registers(ctx, 0x0001, 2, re_reg);

> 機器製造番号の項目
> 入力レジスタ:30030
> 相対アドレス:0x001D
> ワード数:2
> バイト数:4

こちらは
uint16_t re_reg[2];
modbus_read_input_registers(ctx, 0x001D, 2, re_reg);

> 「メーターへ送信する相対アドレスは入力レジスタから30001を引いた 16 進数 2 桁。」とあり、
この説明の解釈は、
電力量のレジスタでは、
30002 - 30001 = 1 = 0x0001
機器製造番号のレジスタでは、
30030 - 30001 = 29 = 0x001D
ということだと思いますので、これをそのまま当てはめると
上に書いた
modbus_read_input_registers(ctx, 0x0001, 2, re_reg);

modbus_read_input_registers(ctx, 0x001D, 2, re_reg);
のレジスタアドレス部分は、それぞれ
modbus_read_input_registers(ctx, 30002 - 30001, 2, re_reg);

modbus_read_input_registers(ctx, 30030 - 30001, 2, re_reg);
でもOKです。
こちらの書き方の方が、あとでこのコードを見たときに、
わかりやすいかもしれません。

たぶん、これで読めるのではないかと思います。

--
なかむら

kumikoohashi

2018年9月3日 2時24分

中村様

お世話になっております。大橋です。

具体的に教えてくださってありがとうございます。
このように書いてみて、取得できなければ、機器の設定など見直します。

アドレス考え方も参考になりました。
しかし、絶対アドレスの30001は意識しなくても、機器に送れば自動的にそこからの相対でデータを戻してくれるということですか。

よろしくお願いいたします。

y.nakamura

2018年9月3日 3時42分

中村です。

> しかし、絶対アドレスの30001は意識しなくても、機器に送れば自動的にそこからの相対でデータを戻してくれるということですか。

大橋さんの最初の質問投稿で、
機器の仕様書に
> 電力量の項目
> 入力レジスタ:30002
> 相対アドレス:0x0001
...
> 「メーターへ送信する相対アドレスは入力レジスタから30001を引いた 16 進数 2 桁。」とあり、
と書いてある、とのことでした。

これを素直に読めば、
「送信する相対アドレスは・・・」
ですから送信するのは相対アドレスであって、
その相対アドレスは「入力レジスタから30001を引いた」値。

で、それをこのままMODBUSの送信バイト列にすると
> 05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)
となると機器の仕様書に書いてあると。

ここに30002は出てきません。

http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
の 8/50 ページの Figure 8 あたりを見るといいかも、です。

別の方からmodbus_mapping_new_start_address()のご指摘がありましたが、
私が使ったことがあるMODBOS機器では、これを使う必要はありませんでした。
そのMODBUS機器もマニュアルにはレジスタは大きな値が書いてありましたが、
libmodbusの関数に渡したのは相対アドレスでした。

--
なかむら

kumikoohashi

2018年9月3日 13時22分

中村様

お世話になっております。大橋です
教えていただいたコードで、値がとれるようになりました。
リンクのPDFの図も勉強になりました。modbusの構造体のようなもので、04コマンドで取得できるデータの、相対アドレスの0と、絶対アドレスの30001を結びつけているんですね。
これまで、別の機器でも表のレジスタのアドレスに一定値を足して取得、と書いてある機器を使った時の不条理さが、腑に落ちました。
他のページも読ませていただきます。
ありがとうございました。

koshiba

2018年8月31日 17時56分

koshibaと申します。
初めての投稿で1件誤って投稿してしまいました。
申し訳ございません。

アドレス等があっている前提で思いつくところがございましたので
投稿させていただきました。

>ctx = modbus_new_rtu(略);

のあとあたりに
libmdbusの標準関数のmodbus_mapping_new_start_address関数で
絶対アドレスの情報をmodbus_mapping_t構造体にセットしていないため
最終的に正しいアドレスに対するリクエストが行われていないのではと感じました。

もし、誤っておりましたら申し訳ございませんが一度ご確認ください。

kumikoohashi

2018年9月3日 2時32分

koshiba様

お世話になっております。大橋です。
ご返信くださってありがとうございます。

> libmdbusの標準関数のmodbus_mapping_new_start_address関数で
> 絶対アドレスの情報をmodbus_mapping_t構造体にセットしていないため
> 最終的に正しいアドレスに対するリクエストが行われていないのではと感じました。

行っておりません。

modbus_mapping_new_start_address(0, 0, 0, 0, 0, 0, 30001, 0);

これだけを書けば良いのでしょうか。ctxへの設定などはいりませんか。
検索したのですがこの関数の例文が見当たらなくて、公式のドキュメントもマッピングの部分だけのソースコードなので、不安です。
http://libmodbus.org/docs/v3.1.3/modbus_mapping_new_start_address.html
使うのがmodbus_read_input_registersなので、引数のint start_input_registersを指定しました。

よろしくお願いいたします。

koshiba

2018年9月3日 10時06分

To:大橋様

小柴です。

改めて状況を確認しますと、中村様のご回答(送信コマンドの見直し)の方が
参考になり、私の回答が却って混乱を招いてしまったかもしれませんが
ご質問頂いたmodbus_mapping_new_start_addressの仕様について
自分の理解している内容を返答いたします。

>modbus_mapping_new_start_address(0, 0, 0, 0, 0, 0, 30001, 0);

大橋様の例にあてはめると
modbus_mapping_new_start_address(0, 0, 0, 0, 0, 0, 30001, N);
→Nは読み出しレジスタ数(ワード単位)
となるか思います。

>これだけを書けば良いのでしょうか。ctxへの設定などはいりませんか。

ctxへの設定は必要ないかと思います。
modbus_mapping_new_start_addressの戻り値となるmodbus_mapping_tの変数を
送信関数(例えばmodbus_reply等)の引数にポインタをセットすれば
modbus_mapping_new_start_addressでセットした絶対アドレスをコマンドに
反映することができます。

絶対アドレスが必要な際に参考いただければ幸いです。

以上、よろしくお願いいたします。

kumikoohashi

2018年9月3日 13時43分

小柴様

お世話になっております。
大橋です

libmodbusの関数をreadやwriteしか使ったことがないのですが、
それでできないことをするときのために、今回教えていただいたことを覚えておきます。
汎用的でない独自コマンドが使える機器などでしょうか(前にありました)。

教えてくださって、ありがとうございました。