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のサイトにあるものや他のサイトも調べましたがわからなかったので、どなたか教えていただけないでしょうか。
コメント
kumikoohashi
古関様
お世話になります。
> 対向機側のスレーブアドレス設定があっている前提にお話しします。
仕様書の、アドレス:01h〜0FH これをスレーブ番号として良いんですよね。
例文"05(アドレス) 04(コマンド) 0001(相対アドレス) 0002(ワード) 218F(CRC)"だと、頭の05のところです。
仕様書に「スレーブ」という用語がないので、戸惑っています。
>
> modbus_read_input_registers()の第3引数はバイトじゃなくてワード指定ではないでしょうか。
ご指摘ありがとうございます。
そう修正しました。
よろしくお願いいたします。
koshiba
> お世話になっております。大橋です。
> 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
中村です。
> データの取得について、仕様にはこう書いてあります。アドレスは、連結する機器一台ずつに連番で設定しています。
> アドレス: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
y.nakamura
中村です。
> しかし、絶対アドレスの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
koshiba
koshibaと申します。
初めての投稿で1件誤って投稿してしまいました。
申し訳ございません。
アドレス等があっている前提で思いつくところがございましたので
投稿させていただきました。
>ctx = modbus_new_rtu(略);
のあとあたりに
libmdbusの標準関数のmodbus_mapping_new_start_address関数で
絶対アドレスの情報をmodbus_mapping_t構造体にセットしていないため
最終的に正しいアドレスに対するリクエストが行われていないのではと感じました。
もし、誤っておりましたら申し訳ございませんが一度ご確認ください。
kumikoohashi
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
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
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