mod bus _ build _ request 는 Mod bus 요청 프레 임을 수 동 으로 구성 하는 lib mod bus 라이 브 러 리의 기본 함 수 입니다 .대부분의 경우 개발 자는 mod bus _ read _ register s 및 mod bus _ write _ bit 와 같은 상 위 레벨 함 수를 사용하여 Mod bus 통신 을 수행 할 수 있지만 특정 특수 시 나 리오 (사 용 자 정의 기능 코드 , 프로토 콜 디 버 그 , 비 표 준 기능 구현 과 같은) 에서는 요청 프레 임을 수 동 으로 빌 드 해야합니다 .이 문서 에서는 mod bus _ build _ request 함 수의 프로토 타입 , 매 개 변 수의 의미 , 사용 방법 및 전체 예 제 코드를 자세히 설명 합니다 .
1) 함 수 프로토 타 입
int mod bus _ build _ request (
mod bus _ t * c t x , Mod bus 컨 텍 스트 객 체
int 함 수 , Mod bus 기능 코드
int ad dr , / / 시작 주소
int nb , / / 수 량 / 숫자
u int 8 _ t * re q / / 출 력 요청 프레 임 버 퍼
);
1. 1 매 개 변 수 상세 설명
| parameter | ||
|---|---|---|
| ctx | ||
| function | ||
| addr | ||
| nb | ||
| req |
1. 2 반환 값
- 成功
- 失敗
기능 코드 와 매 개 변 수의 대응 관계
다른 함 수 코드 에서 , add r 및 nb 매 개 변 수의 의미는 다릅니다 :
| function code | |||
|---|---|---|---|
| 0x01 | |||
| 0x02 | |||
| 0x03 | |||
| 0x04 | |||
| 0x05 | |||
| 0x06 | |||
| 0x0F | |||
| 0x10 |
프레 임 구조 요청
mod bus _ build _ request 에서 빌 드 하는 요청 프레 임 형 식은 사용 중 인 프로토 콜 모 드에 따라 달라 집니다 .
3. 1 Mod bus TCP 모 드 (M BA P 헤 더 + PD U)
TCP 요청 프레 임 구조 (읽 기 및 유지 레 지 스터 의 경우 총 12 바 이트)
// +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
// |트 랜 잭 션 ID 높 음| 트 랜 잭 션 ID 가 낮 습니다 .| 프로토 콜 ID 높 음| 프로토 콜 ID 가 낮 음| 길이 높 음| 길이 낮 음| 슬 레이 브 주소|기능 코드|주소 높 음| 주소 낮 음| 수 량이 높 다 .| 수 량이 낮 다 .|
// +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
// |0 x 00 | 0 x 01 | 0 x 00 | 0 x 00 | 0 x 00 | 0 x 06 | 0 x 01 | 0 x 03 | 0 x 00 | 0 x 00 | 0 x 00 | 0 x 0 a |
// +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
/ / MBA P 헤 더 (7 바 이트):
트 랜 잭 션 식별 자 (2 바 이트): 요청 과 응답 을 일치 시키는 데 사용됩니다 .
- 프로토 콜 식별 자 (2 바 이트): Mod bus 프로토 콜 은 0 x 00 00 입니다 .
// - 길이 (2 바 이트): 후 속 바 이트 수 (스 레이 브 주소 + PD U)
// - 슬 레이 브 주소 (1 바 이트): Unit ID
PD U (Pr oto col Data Unit):
// - 기능 코드 (1 바 이트)
// - 데이터 (N 바 이트)
Mod bus R TU 모 드 (Add ress + PD U + CR C)
/ / R TU 요청 프레 임 구조 (읽 기 및 유지 레 지 스터 를 예 로 들어 총 8 바 이트)
// +--------+--------+--------+--------+--------+--------+--------+--------+
// |슬 레이 브 주소|기능 코드|주소 높 음| 주소 낮 음| 수 량이 높 다 .| 수 량이 낮 다 .| 낮은 CR C |CR C 높 음 |
// +--------+--------+--------+--------+--------+--------+--------+--------+
// |0 x 01 | 0 x 03 | 0 x 00 | 0 x 00 | 0 x 00 | 0 x 0 a | 0 x C 5 | 0 x CD |
// +--------+--------+--------+--------+--------+--------+--------+--------+
/ / R TU 프레 임 = 슬 레이 브address(1バイト) + PDU + CRC16(2バイト)
// CRC低バイト数前に,ハイバイト在后
四、基础使用例の例
4.1 构建リードホールドレジスタリクエスト
#include <stdio.h>
#include <stdlib.h>
#include <modbus.h>
int main() {
modbus_t *ctx;
uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
int req_len;
int i;
// 作成TCPContextより
ctx = modbus_new_tcp("192.168.1.100", 502);
if (ctx == NULL) {
fprintf(stderr, "作成Contextより失敗\n");
return -1;
}
// 設定駅からの住所
modbus_set_slave(ctx, 1);
// 构建リードホールドレジスタリクエスト:address0,read10个
req_len = modbus_build_request(ctx, 0x03, 0, 10, req);
if (req_len == -1) {
fprintf(stderr, "构建リクエスト失敗:%s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
// 打印リクエストフレーム(hexadecimal)
printf("リクエストフレーム長さ:%dbyte\n", req_len);
printf("リクエストフレーム内容:");
for (i = 0; i < req_len; i++) {
printf("%02X ", req[i]);
}
printf("\n");
// 出力サンプル(TCPモード):
// リクエストフレーム長さ:12バイト
// リクエストフレーム内容:00 01 00 00 00 06 01 03 00 00 00 0A
modbus_free(ctx);
return 0;
}
4.2 构建シングルコイルを書く。リクエスト
// 构建シングルコイルを書く。リクエスト:address0,吸合(0xFF00)
req_len = modbus_build_request(ctx, 0x05, 0, 0xFF00, req);
printf("写コイルリクエスト:");
for (i = 0; i < req_len; i++) printf("%02X ", req[i]);
printf("\n");
// 出力:00 01 00 00 00 06 01 05 00 00 FF 00
// 构建シングルコイルを書く。リクエスト:address0,断开(0x0000)
req_len = modbus_build_request(ctx, 0x05, 0, 0x0000, req);
// 出力:00 01 00 00 00 06 01 05 00 00 00 00
4.3 构建単一レジスタの書き込みリクエスト
// 构建単一レジスタの書き込みリクエスト:address0,写入1234
req_len = modbus_build_request(ctx, 0x06, 0, 1234, req);
printf("写レジスタリクエスト:");
for (i = 0; i < req_len; i++) printf("%02X ", req[i]);
printf("\n");
// 出力:00 01 00 00 00 06 01 06 00 00 04 D2
// 1234 = 0x04D2
五、手动リクエストの送信与受信レスポンス
构建リクエストフレーム后,可以使用modbus_send和modbus_receive手动送信和受信:
#include <stdio.h>
#include <modbus.h>
int main() {
modbus_t *ctx;
uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH];
int req_len, rsp_len;
ctx = modbus_new_tcp("192.168.1.100", 502);
modbus_set_slave(ctx, 1);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "接続の失敗:%s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
// 1. 构建リードホールドレジスタリクエスト
req_len = modbus_build_request(ctx, 0x03, 0, 10, req);
printf("リクエスト長さ:%d\n", req_len);
// 2. return response
int sent = modbus_send(ctx, req, req_len);
if (sent != req_len) {
fprintf(stderr, "送信失敗\n");
modbus_close(ctx);
modbus_free(ctx);
return -1;
}
// 3. 受信レスポンス
rsp_len = modbus_receive(ctx, rsp);
if (rsp_len == -1) {
fprintf(stderr, "受信失敗:%s\n", modbus_strerror(errno));
modbus_close(ctx);
modbus_free(ctx);
return -1;
}
// 4. 解析レスポンス(TCPモード,跳过MBAP Head7バイト)
printf("レスポンス長さ:%d\n", rsp_len);
printf("function code:0x%02X\n", rsp[7]);
if (rsp[7] == 0x03) {
// リードホールドレジスタレスポンス
// rsp[7] = function code
// rsp[8] = byte count
// rsp[9...] = register data(すべてのレジスタ。2バイト,前の高さ。)
int byte_count = rsp[8];
int reg_count = byte_count / 2;
printf("number of registers:%d\n", reg_count);
for (int i = 0; i < reg_count; i++) {
uint16_t value = (rsp[9 + i*2] << 8) | rsp[9 + i*2 + 1];
printf("register%d:%d\n", i, value);
}
} else if (rsp[7] & 0x80) {
// Exception Response(機能コード最高位为1)
printf("Exception Response,Exception Code:0x%02X\n", rsp[8]);
}
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
六、自定义機能コード実装
modbus_build_request的一个重要用途是実装デバイス厂商自定义的機能コード。以下サンプル実装一个自定义機能コード0x43(読み取りデバイス序列号):
// 自定义機能コード:読み取りデバイス序列号
// Request:function code0x43,サブ機能コード0x01
// Response:function code0x43,サブ機能コード0x01,序列号長さ,序列号データ
int read_device_serial(modbus_t *ctx, uint8_t *serial, int max_len) {
uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH];
int req_len, rsp_len;
// 注意:modbus_build_request只能构建標準機能コード
// 对于自定义機能コード,需要手动构建PDU部分
// 手动构建TCPリクエストフレーム
// MBAP Head
req[0] = 0x00; req[1] = 0x01; // トランザクションID
req[2] = 0x00; req[3] = 0x00; // ProtocolID
req[4] = 0x00; req[5] = 0x04; // Length(slave address+function code+サブ機能コード)
req[6] = 0x01; // slave address(需与modbus_set_slave設定一致,libmodbus无公开getter)
// PDU
req[7] = 0x43; // 自定义機能コード
req[8] = 0x01; // サブ機能コード:读序列号
req_len = 9;
// return response
modbus_send(ctx, req, req_len);
// 受信レスポンス
rsp_len = modbus_receive(ctx, rsp);
if (rsp_len == -1) return -1;
// 解析レスポンス(跳过MBAP Head7バイト)
if (rsp[7] == 0x43 && rsp[8] == 0x01) {
int serial_len = rsp[9];
if (serial_len > max_len) serial_len = max_len;
memcpy(serial, &rsp[10], serial_len);
return serial_len;
}
return -1;
}
// 使用例の例
int main() {
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
modbus_connect(ctx);
uint8_t serial[32];
int len = read_device_serial(ctx, serial, sizeof(serial));
if (len > 0) {
printf("デバイス序列号:");
for (int i = 0; i < len; i++) printf("%02X ", serial[i]);
printf("\n");
}
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
七、RTUモード下的使用
#include <modbus.h>
#include <modbus-rtu.h>
int main() {
modbus_t *ctx;
uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];
uint8_t rsp[MODBUS_RTU_MAX_ADU_LENGTH];
int req_len, rsp_len;
// 作成RTUContextより
ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);
modbus_set_slave(ctx, 1);
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
modbus_connect(ctx);
// 构建リードコイルを読むリクエスト(RTUモード会自动追加CRC)
req_len = modbus_build_request(ctx, 0x01, 0, 8, req);
printf("RTUリクエスト長さ:%d\n", req_len);
printf("RTURequest frame: ");
for (int i = 0; i < req_len; i++) printf("%02X ", req[i]);
printf("\n");
// 出力(8バイト):01 01 00 00 00 08 3D C9
// 最后2バイト3D C9是CRC16チェック
// 送信并受信
modbus_send(ctx, req, req_len);
rsp_len = modbus_receive(ctx, rsp);
// RTUResponse frame: slave address + function code + data + CRC
if (rsp[1] == 0x01) {
int byte_count = rsp[2];
printf("コイルの状態Bytesの数:%d\n", byte_count);
for (int i = 0; i < byte_count; i++) {
printf("byte%d:0x%02X\n", i, rsp[3 + i]);
}
}
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
八、デバッグ与バッグの分析。
modbus_build_request在协议デバッグ中非常有用,可以打印出完全的リクエストフレーム用于分析:
// デバッグ函数:打印リクエストフレーム詳細
void print_request_detail(modbus_t *ctx, int function, int addr, int nb) {
uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
int len = modbus_build_request(ctx, function, addr, nb, req);
printf("=== リクエストフレーム詳細 ===\n");
printf("function code:0x%02X\n", function);
printf("address:%d (0x%04X)\n", addr, addr);
printf("Quantity/Value:%d (0x%04X)\n", nb, nb);
printf("フレーム長さ:%dbyte\n", len);
// TCPモード解析MBAP Head
if (len >= 7) {
uint16_t trans_id = (req[0] << 8) | req[1];
uint16_t proto_id = (req[2] << 8) | req[3];
uint16_t length = (req[4] << 8) | req[5];
uint8_t slave_id = req[6];
printf("トランザクションID:%d\n", trans_id);
printf("ProtocolID:%d\n", proto_id);
printf("長さ字段:%d\n", length);
printf("slave address:%d\n", slave_id);
}
printf("raw data:");
for (int i = 0; i < len; i++) printf("%02X ", req[i]);
printf("\n\n");
}
// 使用
print_request_detail(ctx, 0x03, 0, 10); // read holding registers
print_request_detail(ctx, 0x05, 0, 0xFF00); // 写コイル
print_request_detail(ctx, 0x06, 5, 100); // 写レジスタ
九、相关函数
| 函数 | Function |
|---|---|
| modbus_send() | リクエストの送信フレーム |
| modbus_receive() | 受信レスポンスフレーム |
| modbus_reply() | 駅からのモデル下回复リクエスト |
| modbus_read_registers() | 高层函数:read holding registers(内部调用build_request) |
| modbus_write_bit() | 高层函数:write single coil |
| modbus_write_register() | 高层函数:write single register |
| modbus_set_slave() | 設定駅からの住所 |
| modbus_strerror() | 取得エラー説明 |
十、common problems
- 戻る-1:チェックctxありますか?效,機能コード是否受サポート,バッファ是否足够大
- リクエストフレーム長さ正しくない:確認プロトコルモデル(TCP/RTU),TCP有7バイトMBAP Head,RTU有2バイトCRC
- デバイスが応答しません:チェック駅からの住所是否正确,関数コードとアドレス是否在デバイスサポート範囲内
- CRCError:RTUモード下modbus_build_request会自动Calculate CRC,不要手动追加
- トランザクションID不マッチング:TCPモード下每次调用会自动递增トランザクションID,无需手动管理
- 自定义機能コード:modbus_build_requestサポートなし。自定义機能コード,需手动构建PDU
- 0x0F/0x10機能コード:modbus_build_request仅サポート01-06機能コード,write multiple coils/レジスタ需手动构建完全フレーム(含データバイト)
- modbus_get_slavedoes not exist:libmodbus无公开駅からの住所getter函数,需自行记录設定的值
modbus_build_request是libmodbus库中理解Modbusプロトコルフレーム構造的关键函数。経由它,開発者たちは可以深入了解Modbusリクエスト的构建过程,実装自定义機能コード、协议デバッグ和底层优化。在实际开发中,建议优先使用高层函数(如modbus_read_registers),只有在特殊需求下才使用modbus_build_request手动构建リクエストフレーム。
Leave a Reply