modbus _ build _ request 함수 프로토 타입

무료무료 기술 자료

이 글은 바로 읽을 수 있으며 기초 학습과 검색 유입에 적합합니다.

modbus _ build _ request 함수 프로토 타입

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 매 개 변 수 상세 설명

매개변수
ctx
function
addr
nb
req

1. 2 반환 값

  • 成功
  • 失败

기능 코드 와 매 개 변 수의 대응 관계

다른 함 수 코드 에서 , add r 및 nb 매 개 변 수의 의미는 다릅니다 :

기능 코드
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 데이터 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 프레 임 = 슬 레이 브주소(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;

    // 创建TCP上下文
    ctx = modbus_new_tcp("192.168.1.100", 502);
    if (ctx == NULL) {
        fprintf(stderr, "创建上下文失败\n");
        return -1;
    }

    // 设置슬레이브 주소
    modbus_set_slave(ctx, 1);

    // 构建读유지 레지스터请求:주소0,읽기10个
    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;
    }

    // 打印请求帧(16진수)
    printf("请求帧길이:%d바이트\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 构建写单个코일请求

// 构建写单个코일请求:주소0,吸合(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

// 构建写单个코일请求:주소0,断开(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 构建写单个레지스터请求

// 构建写单个레지스터请求:주소0,쓰기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. 보내기请求
    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头7바이트)
    printf("响应길이:%d\n", rsp_len);
    printf("기능 코드:0x%02X\n", rsp[7]);
    
    if (rsp[7] == 0x03) {
        // 读유지 레지스터响应
        // rsp[7] = 기능 코드
        // rsp[8] = 바이트数
        // rsp[9...] = 레지스터데이터(每个레지스터2바이트,高바이트在前)
        int byte_count = rsp[8];
        int reg_count = byte_count / 2;
        printf("레지스터 수량:%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("레지스터%d:%d\n", i, value);
        }
    } else if (rsp[7] & 0x80) {
        // 异常响应(기능 코드最高位为1)
        printf("异常响应,异常码:0x%02X\n", rsp[8]);
    }

    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}

六、自定义기능 코드实现

modbus_build_request的一个重要用途是实现设备厂商自定义的기능 코드。以下예제实现一个自定义기능 코드0x43(읽기设备시퀀스号):

// 自定义기능 코드:읽기设备시퀀스号
// 请求:기능 코드0x43,子기능 코드0x01
// 响应:기능 코드0x43,子기능 코드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头
    req[0] = 0x00; req[1] = 0x01;  // 事务ID
    req[2] = 0x00; req[3] = 0x00;  // 프로토콜ID
    req[4] = 0x00; req[5] = 0x04;  // 길이(슬레이브 주소+기능 코드+子기능 코드)
    req[6] = 0x01; // 슬레이브 주소(需与modbus_set_slave设置一致,libmodbus无公开getter)
    // PDU
    req[7] = 0x43;  // 自定义기능 코드
    req[8] = 0x01;  // 子기능 코드:读시퀀스号
    
    req_len = 9;
    
    // 보내기请求
    modbus_send(ctx, req, req_len);
    
    // 수신响应
    rsp_len = modbus_receive(ctx, rsp);
    if (rsp_len == -1) return -1;
    
    // 분석响应(跳过MBAP头7바이트)
    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;

    // 创建RTU上下文
    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("RTU请求帧:");
    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);
    
    // RTU响应帧:슬레이브 주소 + 기능 코드 + 데이터 + CRC
    if (rsp[1] == 0x01) {
        int byte_count = rsp[2];
        printf("코일 상태바이트数:%d\n", byte_count);
        for (int i = 0; i < byte_count; i++) {
            printf("바이트%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("기능 코드:0x%02X\n", function);
    printf("주소:%d (0x%04X)\n", addr, addr);
    printf("数量/值:%d (0x%04X)\n", nb, nb);
    printf("帧길이:%d바이트\n", len);
    
    // TCP모드분석MBAP头
    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("프로토콜ID:%d\n", proto_id);
        printf("길이필드:%d\n", length);
        printf("슬레이브 주소:%d\n", slave_id);
    }
    
    printf("原始데이터:");
    for (int i = 0; i < len; i++) printf("%02X ", req[i]);
    printf("\n\n");
}

// 使用
print_request_detail(ctx, 0x03, 0, 10);   // 读유지 레지스터
print_request_detail(ctx, 0x05, 0, 0xFF00); // 写코일
print_request_detail(ctx, 0x06, 5, 100);   // 写레지스터

九、相关函数

函数功能
modbus_send()보내기请求帧
modbus_receive()수신响应帧
modbus_reply()슬레이브모드下回复请求
modbus_read_registers()高层函数:读유지 레지스터(内部调用build_request)
modbus_write_bit()高层函数:写单个코일
modbus_write_register()高层函数:写单个레지스터
modbus_set_slave()设置슬레이브 주소
modbus_strerror()获取错误설명

十、常见문제

  • 返回-1:检查ctx是否有效,기능 코드是否受支持,缓冲区是否足够大
  • 请求帧길이不对:确认프로토콜모드(TCP/RTU),TCP有7바이트MBAP头,RTU有2바이트CRC
  • 设备无响应:检查슬레이브 주소是否正确,기능 코드和주소是否在设备支持范围内
  • CRC错误:RTU모드下modbus_build_request会自动계산CRC,不要手动추가
  • 事务ID不일치:TCP모드下每次调用会自动递增事务ID,无需手动管理
  • 自定义기능 코드:modbus_build_request不支持自定义기능 코드,需手动构建PDU
  • 0x0F/0x10기능 코드:modbus_build_request仅支持01-06기능 코드,写多个코일/레지스터需手动构建전체 프레임(含데이터바이트)
  • modbus_get_slave不存在:libmodbus无公开的슬레이브 주소getter函数,需自行记录设置的值

modbus_build_request是libmodbus库中理解Modbus프로토콜帧结构的关键函数。通过它,개발者可以深入了解Modbus请求的构建过程,实现自定义기능 코드、프로토콜디버깅和底层优化。在实际개발中,建议优先使用高层函数(如modbus_read_registers),只有在特殊需求下才使用modbus_build_request手动构建请求帧。

실제 프로젝트에 이 자료를 활용하시겠습니까?

도구 센터에서 프레임 분석, CRC 검증, 장비 디버깅을 진행하거나 요청을 제출하여 선정 및 연동 제안을 받아보세요.

엔지니어 회원

이 글을 실행 가능한 디버깅 자료로 활용하세요

가입하면 고급 프레임 분석, 자료 패키지 다운로드, 코드 예제, 엔지니어링 사례, 우선 기술 지원을 이용할 수 있어 실제 프로젝트 납품에 적합합니다.

고급 도구 무제한 이용
자료 패키지와 코드 패키지
완전한 엔지니어링 사례 라이브러리
우선 기술 지원 채널

답글 작성

이메일 주소는 공개되지 않습니다. 必填项已用 * 标注