本論文ではLinux 上でlibmodbusライブラリに基づいてModbus RTUを開発し,実験を行う前にlibmodbusライブラリと仮想シリアルポートを実現するためのsocatツールをインストールする必要がある。
準備作業の準備
libmodbusのインストール
押すことができる。 libmodbusソフトウェア·ライブラリ の“インストール”セクションの手順で手動でコンパイルしてインストールします。
Socatのインストール
sudo apt install socatSocatは強力なネットワークツールであり、この実験では2つの相互接続された仮想シリアルポートを実装するために使用されるため、実際のシリアルポートデバイスを準備する必要はありません。石鹸の詳細については、こちらをご覧ください。 socatコマンド。
コードの実装
完全なコードは https://github.com/getiot/linux-c/tree/main/library/libmodbus 取得します。
Modbus RTUスレーブ
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include "modbus.h" //libmodbusライブラリを参照
#define Loop 1 //サイクルの数
#define server_ID 17 //スレーブデバイスアドレス
#define ADDRESS_START 0 //テストレジスタの開始アドレス
#define ADDRESS_END 99 //テストレジスタの終了アドレス
int main int argc char *argv]
{
mod_t *ctx = NULL;
int nb = 0; //テストするレジスタの数
uint8_t* tab_rq_bits; //送受信したデータの保存(以下同じ)
uint8_t* tab_rp_bits;
uint16_t * tab_rq_registers;
uint16_t * tab_rw_rq_registers;
uint16_t *tab_rp_registers;
CHAR * ポート;
mod_m_t *mb_m;
if argc == 2 {
port = argv 1];
(エルゼ)
port = "COM5";
}
//RTU型コンテナの作成
ctx = modbus_new_rtu(port,19200,'N',8,1);
//スレーブアドレスを設定
mod_set_slave ctx、Server_ID;
//デバッグモードの設定
mod_set_ctx true;
// RTUモードではシリアルポートを開く
if mod_ctx == -1
{
ff stderr "Connection ed %s \n" mod_strerror no;
mod_free ctx;
リターン-1;
}
//レジスタデータを格納するために4つのメモリ領域を要求し、500のレジスタアドレスを要求します。
mb_m = mod_m_new 500 500 500 500;
if mb_m == NULL {
ff stderr "Error m %s\n" mod_strerror no;
mod_free ctx;
リターン-1;
}
//クエリフレームをループしてメッセージを返信
for;;{
uint8_t query MODBUS_TCP_MAX_ADU_LENGTH];
rc = 0;
rc = mod_receive ctx;//クエリメッセージを取得
if rc = 0 {
Rcは量子サイズです。
mod_rectx rc mb_m; //応答メッセージの返信
}
else {
//クライアントまたはエラーによる接続のクローズ
f
}
}
f
メモリを解放する。
mod_m_free mb_m;
mod_clctx;
mod_free ctx;
リターン0;
}Modbus RTUマスター
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include "modbus.h" //libmodbusライブラリを参照
#define Loop 1 //サイクルの数
#define server_ID 17 //スレーブデバイスアドレス
#define ADDRESS_START 0 //テストレジスタの開始アドレス
#define ADDRESS_END 99 //テストレジスタの終了アドレス
int main int argc char *argv]
{
// printf "%c" 0b01000001; // A
mod_t *ctx = NULL;
int nb = 0; //テストするレジスタの数
uint8_t* tab_rq_bits; //送受信したデータの保存(以下同じ)
uint8_t* tab_rp_bits;
uint16_t * tab_rq_registers;
uint16_t * tab_rw_rq_registers;
uint16_t *tab_rp_registers;
CHAR * ポート;
if argc == 2 {
port = argv 1];
(エルゼ)
port = "COM4";
}
//RTU型コンテナの作成
ctx = modbus_new_rtu(port,19200,'N',8,1);
//スレーブアドレスを設定
mod_set_slave ctx、Server_ID;
//デバッグモードの設定
mod_set_ctx true;
// RTUモードではシリアルポートを開く
if modbus_connect ctx == -1 {
ff stderr "Connection ed %s \n" mod_strerror no;
mod_free ctx;
リターン-1;
}
printf "Connection successed\n";
/* 異なるメモリ領域の割り当てと初期化 */
//テストが必要なレジスタの数を数える
nb = ADDRESS_END-ADDRESS_START
// 以下のアプリケーションメモリブロック、水着は送受信各データを保存
tab_rq_bits = uint8_t * malnb * of uint8_t;
memset tab_rq_bits 0 nb*sizeof uint8_t;
tab_rp_bits = uint8_t * malnb * of uint8_t;
memset tab_rp_bits 0 nb* of uint8_t;
tab_rq_registers = uint16_t * malloc nb * sizeof uint16_t;
memset tab_rq_registers 0 nb*sizeof uint16_t;
tab_rw_rq_registers = uint16_t * malloc nb * sizeof uint16_t;
memset tab_rw_rq_registers 0 nb* of uint16_t;
tab_rp_registers = uint16_t * malnb * of uint16_t;
memset tab_rp_registers 0 nb* of uint16_t;
int nb_= 0、nb_fail = 0、= 0、rc = 0;
while nb_loop ++ Loop {
//開始アドレスからの順次テスト
for = ADDRESS_START; ADDRESS_END;++{
sleep(1);
int i = 0;
//テスト用の乱数の生成
for i = 0; i nb; i++{
tab_rq_registers i] = uint16_t 65535.0 * rand/RAND_MAX + 1.0;
tab_rw_rq_registers i] = ~tab_rq_registers i];
tab_rq_bits i] = tab_rq_registers i] % 2;
}
nb = ADDRESS_END-addr;
//コイルレジスタの単一読み取りおよび書き込みテスト
printf "modbus_write_bit... N 'N';
rc = mod__bit ctx tab_rq_bits 0]; //コイルレジスタへの书き込み
if rc != 1) {
printf "ERROR modbus_write_bit % d\n" rc;
printf "Address = %d value = %d \n" addr tab_rq_bits0];
nB_fail ++;
}
else {
//書き込みの後、読み取りと比較
rc = mod_read_bits ctx 1 tab_rp_bits;
if rc != 1つの||tab_rq_bits[ 0] != tab_rp_bits0]{
f
printf "address = %d\n" addr;
NB_fail ++;
}
}
//テストコイルレジスタのバッチ読み取り/書き込み
f N 'N';
rc = mod__bits ctx nb tab_rq_bits;
if rc != nb){
f
f
nB_fail ++;
}
else {
//書き込みの後、読み取りと比較
rc = mod_read_bits ctx nb tab_rp_bits;
if:rc != nb {{
printf "ERROR modbus_read_bits\n";
printf "address = %d nb = %d\n" addr nb;
NB_fail ++;
}
else {
//比較する
for i = 0; i nb; i++{
if tab_rp_bits i] != tab_rq_bits i]{
f
f %d 0x % x\n"、
addr, tab_rq_bits i], tab_rq_bits i],
tab_rp_bits i] tab_rp_bits i];
NB_fail ++;
}
}
}
}
//テストはレジスタの単一の読み書きを保持する
f N 'N';
rc = mod__register ctx tab_rq_registers 0];
if rc != 1) {
f
printf "Address = %d Val = %d 0x % x\n"
addr, tab_rq_registers 0], tab_rq_registers 0]);
NB_fail ++;
}
else {
//書き込み後の読み込み
rc = mod_read_registers ctx 1 tab_rp_registers;
if:rc != 1){
printf "ERROR modbus_read_registers % d\n" rc;
printf "Address = %d\n" addr;
NB_fail ++;
}
else {
読み取り後の比較
if tab_rq_registers 0] != tab_rp_registers 0]{
printf "ERROR modbus_read_registers % d\n" rc;
f %d 0x % x\n"、
addr, tab_rq_registers 0], tab_rq_registers 0],
tab_rp_registers0] tab_rp_registers0];
NB_fail ++;
}
}
}
//テストコイルレジスタのバッチ読み取り/書き込み
f N 'N';
rc = mod__registers ctx nb tab_rq_registers;
if rc != nb){
f
f
NB_fail ++;
}
else {
//Read Testの実行
rc = mod_read_registers ctx nb tab_rp_registers;
if:rc != nb {{
printf "ERROR modbus_read_registers % d\n" rc;
printf "Address = %d\n" addr;
NB_fail ++;
}
else {
for i = 0; i nb; i++{
読み取り後の比較
if tab_rq_registers i] != tab_rp_registers i]{
printf "ERROR modbus_read_registers % d\n" rc;
f %d 0x % x\n"、
addr, tab_rq_registers 0], tab_rq_registers 0],
tab_rp_registers0] tab_rp_registers0];
NB_fail ++;
}
}
}
}
//関数コード23 (0 x 17)複数のレジスタを読み書きするテスト
f N 'N';
rc = mod__and_read_registers ctx
addr nb tab_rw_rq_registers
addr nb tab_rp_registers;
if:rc != nb {{
f
f
NB_fail ++;
}
else {
//読み取りと比較
for i = 0; i nb; i++{
if tab_rp_registers i] tab_rw_rq_registers i]{
printf "ERROR modbus_read_and_write_registers READ\n";
printf "Address = %d value %d 0x %X!= %d 0x %X\n "、
addr, tab_rp_registers i], tab_rw_rq_registers i],
tab_rp_registers i tab_rw_rq_registers i];
NB_fail ++;
}
}
rc = mod_read_registers ctx nb tab_rp_registers;
if rc != nb){
printf "ERROR modbus_read_registers % d\n" rc;
f
}
else {
for i = 0; i nb; i++{
if tab_rw_rq_registers i] != tab_rp_registers i]{
printf "ERROR modbus_read_and_write_registers\n";
f= %d (0x%X)\n",
addr, tab_rw_rq_registers[i], tab_rw_rq_registers[i],
tab_rp_registers[i], tab_rp_registers[i]);
nb_fail++;
}
}
}
}
}
printf("Test: ");
if (nb_fail) {
printf("%d FAILS\n",nb_fail);
} else {
printf("SUCCESS\n");
}
}
// FREE the memory
free(tab_rq_bits);
free(tab_rp_bits);
free(tab_rq_registers);
free(tab_rp_registers);
free(tab_rw_rq_registers);
// close the connection
modbus_close(ctx);
modbus_free(ctx);
return 0;
}Makefile
CC=gcc
CFLAGS=-I/usr/local/include/modbus/ -lmodbus -g
MASTER=modbus-rtu-master
SLAVE=modbus-rtu-slave
all:
(CC) -o(MASTER) (MASTER).c(CFLAGS)
(CC) -o(SLAVE) (SLAVE).c(CFLAGS)
clean:
rm -rf (MASTER)(SLAVE)テストテスト
仮想シリアルポートの起動
$ socat -d -d PTY PTY
2021/07/16 14:36:43 socat[23932] N PTY is /dev/pts/7
2021/07/16 14:36:43 socat[23932] N PTY is /dev/pts/8
2021/07/16 14:36:43 socat[23932] N starting data transfer loop with FDs [5,5] and [7,7]スレーブ装置を起動し、マスターのCommandを待つ
./modbus-rtu-slave /dev/pts/7マスターステーションを起動し、スレーブステーションにCommandを出す
./modbus-rtu-master /dev/pts/8これで、ターミナルからModbus RTUマスターとスレーブ間のデータCommunicationプロセスを見ることができます。
Leave a Reply