一、libmodbus 下载

二、更改modbus.c和modbus.h文件

1、首先在modbus.h文件中增加读写文件记录的函数声明。

java modbus RTU传输 modbus 传输文件_i++

//fileNumber  文件名
//startRecordNumber   起始文件记录号
//fileData    需要写入文件的数据
//length      写入文件的数据长度
MODBUS_API int modbus_write_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
                                        const uint16_t *fileData, uint8_t length);

//fileNumber   文件名
//startRecordNumber  起始文件记录号
//length    需要读取文件数据的长度
//dest      读取到的文件数据存放目标
MODBUS_API int modbus_read_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
                                       uint16_t length, uint16_t *dest);

2、modbus.h文件中增加读写文件宏定义。

java modbus RTU传输 modbus 传输文件_数据_02

#define MODBUS_FC_READ_FILE_RECORD          0x14
#define MODBUS_FC_WRITE_FILE_RECORD         0x15

3、modbus.c文件计算读取数据长度函数中增加文件读写的处理。

java modbus RTU传输 modbus 传输文件_数据_03

if (function <= MODBUS_FC_READ_INPUT_REGISTERS ||
            function == MODBUS_FC_REPORT_SLAVE_ID ||
            function == MODBUS_FC_WRITE_AND_READ_REGISTERS ||
            function == MODBUS_FC_READ_FILE_RECORD ||
            function == MODBUS_FC_WRITE_FILE_RECORD)
        {
            length = msg[ctx->backend->header_length + 1];
        }
        else
        {
            length = 0;
        }

4、在modbus.c文件计算响应数据长度函数中增加。

java modbus RTU传输 modbus 传输文件_java modbus RTU传输_04

case MODBUS_FC_WRITE_FILE_RECORD:
        length = 9 + 2 * (req[offset + 7] << 8 | req[offset + 8]);
        break;
    case MODBUS_FC_READ_FILE_RECORD:
        length = 4 + 2 * (req[offset + 7] << 8 | req[offset + 8]);
        break;

java modbus RTU传输 modbus 传输文件_数据_05


由协议文档可得,写文件收到的回应数据长度应为:

(Record length Hi << 8 | Record length Lo) * 2 + 前面9个除文件数据以外的固定回复数据。

java modbus RTU传输 modbus 传输文件_i++_06


由协议文档可得,读文件收到的回应数据长度应为:

(Record length Hi << 8 | Record length Lo) * 2 + 前面4个除文件数据以外的固定回复数据。5、在校验函数中增加读写文件的处理。

java modbus RTU传输 modbus 传输文件_i++_07


java modbus RTU传输 modbus 传输文件_i++_08

case MODBUS_FC_READ_FILE_RECORD:
            /* 保证req_nb_value 和 rsp_nb_value 计算后的值相等,才会通过校验 */
            req_nb_value = (req[offset + 7] << 8) + req[offset + 8];
            rsp_nb_value = ((rsp[offset + 2] - 1) / 2);
            break;
        case MODBUS_FC_WRITE_FILE_RECORD:
            req_nb_value = (req[offset + 7] << 8) + req[offset + 8];
            rsp_nb_value = (rsp[offset + 7] << 8) | rsp[offset + 8];
            break;

这里写文件发送和回复的数据长度是一样的。

java modbus RTU传输 modbus 传输文件_数据_09


读文件回复数据长度 = (File resp.length - 1) / 2

三、写文件函数定义

int modbus_write_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
                             const uint16_t *fileData, uint8_t length)
{
    int rc;
    int req_length;
    uint8_t req[MAX_MESSAGE_LENGTH];

    if (ctx == NULL || length <= 0) {
        errno = EINVAL;
        return -1;
    }

    if(fileNumber == 0)
    {
        errno = EINVAL;
        return -1;
    }

	//起始文件记录号范围是  0000-9999
    if(startRecordNumber > 0x270F)
    {
        errno = EINVAL;
        return -1;
    }

	//文件传输一次最多传输122个数据
    if( length > 122 )
    {
        errno = EINVAL;
        return -1;
    }

    req_length = ctx->backend->build_request_basis(ctx,
                                                   MODBUS_FC_WRITE_FILE_RECORD,
                                                   0,0,req);

    req[2] = length*2 + 7;                //request Data length
    req[3] = 0x06;                        //reference type must be specified as 6

    req[4] = fileNumber >> 8;             //File Number High
    req[5] = fileNumber & 0x00ff;         //File Number Low

    req[6] = startRecordNumber >> 8;      //The starting record number high within the file
    req[7] = startRecordNumber & 0x00ff;  //The starting record number low within the file

    req[8] = length >> 8;                 //The length of the record to be written
    req[9] = length & 0x00ff;             //The length of the record to be written

    for(int i = 0; i < length; i++)
    {
        req[2*i + 10] = fileData[i] >> 8;
        req[2*i + 11] = fileData[i] & 0x00ff;
    }

    rc = send_msg(ctx, req, length*2 + 10);
    if (rc > 0)
    {
        uint8_t rsp[MAX_MESSAGE_LENGTH];

        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
        if (rc == -1)
            return -1;

        rc = check_confirmation(ctx, req, rsp, rc);
        if (rc == -1)
            return -1;
    }

    return rc;
}

四、读文件函数定义

int modbus_read_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
                            uint16_t length, uint16_t *dest)
{
    int rc;
    int req_length;
    uint8_t req[MAX_MESSAGE_LENGTH];

    if (ctx == NULL || length <= 0) 
    {
        errno = EINVAL;
        return -1;
    }

    if(fileNumber == 0)
    {
        errno = EINVAL;
        return -1;
    }
    
    //起始文件记录号范围是  0000-9999
    if(startRecordNumber > 0x270F)
    {
        errno = EINVAL;
        return -1;
    }

    req_length = ctx->backend->build_request_basis(ctx,
                                                   MODBUS_FC_READ_FILE_RECORD,
                                                   0,0,req);

    req[2] = 7;                           //request Data length
    req[3] = 0x06;                        //reference type must be specified as 6

    req[4] = fileNumber >> 8;             //File Number High
    req[5] = fileNumber & 0x00ff;         //File Number Low

    req[6] = startRecordNumber >> 8;      //The starting record number high within the file
    req[7] = startRecordNumber & 0x00ff;  //The starting record number low within the file

    req[8] = length >> 8;                 //The length of the record to be written
    req[9] = length & 0x00ff;             //The length of the record to be written

	rc = send_msg(ctx, req, 10);
    if (rc > 0)
    {
        int offset;
        int i;
        uint8_t rsp[MAX_MESSAGE_LENGTH];

        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
        if (rc == -1)
            return -1;

        rc = check_confirmation(ctx, req, rsp, rc);
        if (rc == -1)
            return -1;

        offset = ctx->backend->header_length;

        for (i = 0; i < rc; i++)
        {
            /* shift reg hi_byte to temp OR with lo_byte */
            dest[i] = (rsp[offset + 4 + (i << 1)] << 8) |
                rsp[offset + 5 + (i << 1)];
        }
    }

	return rc;
}

五、函数接口用法简单示例
1、写文件。

uint16_t modbusData[122];
for(int i = 0; i < 122; i++)
{
	modbusData[i] = i;
}
modbus_write_file_record(modbus, 1, 1, modbusData,2);

2、读文件。

uint16_t modbusData[100];
modbus_read_file_record(modbus, 1, 1, 2, modbusData)

for(int i = 0; i < 2; i++)
{
	cout << i << ":" << modbusData[i] << endl;
}

六、用串口助手测试

1、写文件测试。

java modbus RTU传输 modbus 传输文件_i++_10


附上写文件回复的示例数据:

01 15 0D 06 00 01 00 01 00 03 4F 60 59 7D FF 01 19 8B2、读文件测试。

java modbus RTU传输 modbus 传输文件_数据_11


附上读文件回复的示例数据:

01 14 06 05 06 4F 60 59 7D 05 71

七、也可以直接下载我改完的modbus.c和modbus.h文件,替换掉原来的文件就可以直接用了