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

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

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

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手动构建リクエストフレーム。

Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.