web-dev-qa-db-ja.com

CでModbusを使用して単純なクライアント/サーバーを作成する

私は現在、さまざまな自動化機能が通信できるようにするプロジェクトに取り組んでいます。そのために、modbusプロトコルを使用して通信するクライアントとサーバーを作成します。とりあえず、ModBus/TCP、ModBus/RTU、ModBus/ASCIIのどちらを使用するのかわかりません。

Cでクライアント/サーバーの例を検索しましたが、ライブラリは見つかりましたが、簡単な通信の例はありませんでした。ライブラリを探しているわけではないので、ゼロから始めたいと思います。

私が求めているのは、誰かがModbusを使用して通信するクライアントまたはサーバー、あるいはその両方のためにCで記述された簡単なコードを私に与えることができるかどうかです。 (RTU/TCP/ASCII)。

シンプルな方が良いほど、たとえば、コードで実証してほしいのは、サーバーへの初期化、要求、応答、接続のクローズです。

お時間をいただき、誠にありがとうございます。

9
PiggyGenius

三つの事:

  1. 独自のクライアントおよびサーバーコンポーネントを開発している場合、Modbusを使用することをお勧めします。つまり、オープン性を重視して厳密に必要または便利な場合のみです(つまり、他のメーカーは、標準化されたプロトコルを使用してクライアントまたはサーバーコンポーネントと通信できる必要があります) -およびModbusフィット)。
  2. Modbus TCPは、TCP/IP上のModbus RTU(/ ASCII)だけではないことに注意してください(もちろん許可されていますが、UDPも許可されます)。いくつかの重要な違いがあります。考慮する。
  3. より深いレベルでModbusを理解する必要があることを理解しています。その時点で、Cプログラム内のオープンシリアルチャネルまたは(リスニング)TCPソケット)を取得したら、単純なModbus要求/応答から始めることができます。

この 短いですが完全な説明 と、この 絶えず更新されるライブラリ のドキュメントもご覧ください。


libmodbus に基づいた、Linux用の非常に簡略化されたRTUの例を次に示します。
コンパクトにするために、C99の緩和を許可してください。
現実の世界では、SIGTERMなどの信号も適切に処理する必要があります...
また、modbus_rtu_set_serial_mode(RS232 vs RS485)Linuxカーネル2.6.28以降の関数。お使いのプラットフォームでRS485の操作を簡単にする他のライブラリが見つかる場合があります。

マスタースニペット

//Create a new RTU context with proper serial parameters (in this example,
//device name /dev/ttyS0, baud rate 9600, no parity bit, 8 data bits, 1 stop bit)
modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}

//Set the Modbus address of the remote slave (to 3)
modbus_set_slave(ctx, 3);


uint16_t reg[5];// will store read registers values

//Read 5 holding registers starting from address 10
int num = modbus_read_registers(ctx, 10, 5, reg);
if (num != 5) {// number of read registers is not the one expected
    fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}

modbus_close(ctx);
modbus_free(ctx);

スレーブスニペット

//Prepare a Modbus mapping with 30 holding registers
//(plus no output coil, one input coil and two input registers)
//This will also automatically set the value of each register to 0
modbus_mapping_t *mapping = modbus_mapping_new(0, 1, 30, 2);
if (!mapping) {
    fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
    exit(1);
}


//Example: set register 12 to integer value 623
mapping->tab_registers[12] = 623;


modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (!ctx) {
    fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
    exit(1);
}

//Set the Modbus address of this slave (to 3)
modbus_set_slave(ctx, 3);


if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    exit(1);
}


uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];// request buffer
int len;// length of the request/response

while(1) {
    len = modbus_receive(ctx, req);
    if (len == -1) break;

    len = modbus_reply(ctx, req, len, mapping);
    if (len == -1) break;
}
printf("Exit the loop: %s\n", modbus_strerror(errno));

modbus_mapping_free(mapping);
modbus_close(ctx);
modbus_free(ctx);
12
matpop