diff --git a/doc/Makefile.am b/doc/Makefile.am index 5a52c0409..55cf89dfc 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -12,7 +12,7 @@ TXT3 = \ modbus_get_float_dcba.txt \ modbus_get_header_length.txt \ modbus_get_response_timeout.txt \ - modbus_get_slave.txt \ + modbus_get_slave.txt \ modbus_get_socket.txt \ modbus_mapping_free.txt \ modbus_mapping_new.txt \ @@ -22,6 +22,7 @@ TXT3 = \ modbus_new_tcp_pi.txt \ modbus_new_tcp.txt \ modbus_read_bits.txt \ + modbus_read_file_record.txt \ modbus_read_input_bits.txt \ modbus_read_input_registers.txt \ modbus_read_registers.txt \ @@ -59,6 +60,7 @@ TXT3 = \ modbus_write_and_read_registers.txt \ modbus_write_bits.txt \ modbus_write_bit.txt \ + modbus_write_file_record.txt \ modbus_write_registers.txt \ modbus_write_register.txt TXT7 = libmodbus.txt diff --git a/doc/modbus_read_file_record.txt b/doc/modbus_read_file_record.txt new file mode 100644 index 000000000..9cbd4f003 --- /dev/null +++ b/doc/modbus_read_file_record.txt @@ -0,0 +1,42 @@ +modbus_read_file_record(3) +========================== + + +NAME +---- +modbus_read_file_record - Read values from a file register + + +SYNOPSIS +-------- +*int modbus_read_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', uint16_t '*dest');* + + +DESCRIPTION +----------- +The *modbus_read_file_record()* function reads _nb_ values from file +register _addr_, starting from position _sub_addr_ in the file register. + +A file register is an array of values. +A modbus device may have up to 65535 file registers, addressed 1 to 65535 +decimal. +Each file register contains 10000 values, addressed 0000 to 9999 decimal. + +This function uses the Modbus function code 0x14 (Read File Record). + + +RETURN VALUE +------------ +The function shall return the number of records read (i.e. the value of _nb_) if successful. +Otherwise it shall return -1 and set errno. + + +SEE ALSO +-------- +linkmb:modbus_read_register[3] +linkmb:modbus_write_file_register[3] + + +AUTHORS +------- +The libmodbus file register documentation was added by Richard Ash at EA Technology. diff --git a/doc/modbus_write_file_record.txt b/doc/modbus_write_file_record.txt new file mode 100644 index 000000000..82b2f4c1c --- /dev/null +++ b/doc/modbus_write_file_record.txt @@ -0,0 +1,42 @@ +modbus_write_file_record(3) +========================== + + +NAME +---- +modbus_write_file_record - Write values to a file register + + +SYNOPSIS +-------- +*int modbus_write_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', const uint16_t '*src');* + + +DESCRIPTION +----------- +The *modbus_write_file_record()* function writes _nb_ values to file +register _addr_, starting from position _sub_addr_ in the file register. + +A file register is an array of values. +A modbus device may have up to 65535 file registers, addressed 1 to 65535 +decimal. +Each file register contains 10000 values, addressed 0000 to 9999 decimal. + +This function uses the Modbus function code 0x15 (Write File Record). + + +RETURN VALUE +------------ +The function shall return the number of records written (i.e. the value of _nb_) if successful. +Otherwise it shall return -1 and set errno. + + +SEE ALSO +-------- +linkmb:modbus_write_register[3] +linkmb:modbus_read_file_register[3] + + +AUTHORS +------- +The libmodbus file register documentation was added by Richard Ash at EA Technology. diff --git a/src/modbus.c b/src/modbus.c index 41a421359..e5bf95100 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -152,6 +152,12 @@ static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t /* The response is device specific (the header provides the length) */ return MSG_LENGTH_UNDEFINED; + case MODBUS_FC_READ_FILE_RECORD: + length = 4 + 2 * (req[offset + 7] << 8 | req[offset + 8]); + break; + case MODBUS_FC_WRITE_FILE_RECORD: + length = 9 + 2 * (req[offset + 7] << 8 | req[offset + 8]); + break; case MODBUS_FC_MASK_WRITE_REGISTER: length = 7; break; @@ -260,6 +266,9 @@ static uint8_t compute_meta_length_after_function(int function, } else if (function == MODBUS_FC_WRITE_MULTIPLE_COILS || function == MODBUS_FC_WRITE_MULTIPLE_REGISTERS) { length = 5; + } else if (function == MODBUS_FC_READ_FILE_RECORD || + function == MODBUS_FC_WRITE_FILE_RECORD) { + length = 8; } else if (function == MODBUS_FC_MASK_WRITE_REGISTER) { length = 6; } else if (function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { @@ -280,6 +289,12 @@ static uint8_t compute_meta_length_after_function(int function, case MODBUS_FC_MASK_WRITE_REGISTER: length = 6; break; + case MODBUS_FC_READ_FILE_RECORD: + length = 3; + break; + case MODBUS_FC_WRITE_FILE_RECORD: + length = 8; + break; default: length = 1; } @@ -304,6 +319,10 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, case MODBUS_FC_WRITE_AND_READ_REGISTERS: length = msg[ctx->backend->header_length + 9]; break; + case MODBUS_FC_READ_FILE_RECORD: + case MODBUS_FC_WRITE_FILE_RECORD: + length = msg[ctx->backend->header_length + 1] - 7; + break; default: length = 0; } @@ -313,6 +332,10 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, function == MODBUS_FC_REPORT_SLAVE_ID || function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { length = msg[ctx->backend->header_length + 1]; + } else if (function == MODBUS_FC_READ_FILE_RECORD) { + length = msg[ctx->backend->header_length + 2] - 1; + } else if (function == MODBUS_FC_WRITE_FILE_RECORD) { + length = msg[ctx->backend->header_length + 1] - 7; } else { length = 0; } @@ -600,6 +623,14 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, /* Report slave ID (bytes received) */ req_nb_value = rsp_nb_value = rsp[offset + 1]; break; + case MODBUS_FC_READ_FILE_RECORD: + 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; default: /* 1 Write functions & others */ req_nb_value = rsp_nb_value = 1; @@ -1236,6 +1267,68 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, return status; } +int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint16_t *dest) +{ + int rc; + int req_length; + uint8_t req[_MIN_REQ_LENGTH]; + uint8_t rsp[MAX_MESSAGE_LENGTH]; + + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + // check if record number is within range + if (sub_addr > MODBUS_MAX_FILE_RECORD_NUMBER) { + if (ctx->debug) { + fprintf(stderr, + "ERROR Too big file record number (%d > %d)\n", + sub_addr, MODBUS_MAX_FILE_RECORD_NUMBER); + } + errno = EMBMDATA; + return -1; + } + + req_length = ctx->backend->build_request_basis(ctx, + MODBUS_FC_READ_FILE_RECORD, + 0, 0, req); + + req_length -= 4; + req[req_length++] = 7; // one request, so 7 bytes + req[req_length++] = 6; + req[req_length++] = addr >> 8; + req[req_length++] = addr & 0x00ff; + req[req_length++] = sub_addr >> 8; + req[req_length++] = sub_addr & 0x00ff; + req[req_length++] = nb >> 8; + req[req_length++] = nb & 0x00ff; + + rc = send_msg(ctx, req, req_length); + if (rc > 0) { + int offset; + int i; + + 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; + // if all went well, copy returned data to caller's pointer, converting + // bytes into 16-bit words. + for (i = 0; i < rc; i++) { + + dest[i] = (rsp[offset + 4 + (i << 1)] << 8) | + rsp[offset + 5 + (i << 1)]; + } + } + + return rc; +} + /* Write a value to the specified register of the remote device. Used by write_bit and write_register */ static int write_single(modbus_t *ctx, int function, int addr, const uint16_t value) @@ -1401,6 +1494,64 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src) return rc; } +int modbus_write_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, const uint16_t *src) +{ + int rc; + int i; + int req_length; + int byte_count; + uint8_t req[MAX_MESSAGE_LENGTH]; + + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + // check record number is valid + if (sub_addr > MODBUS_MAX_FILE_RECORD_NUMBER) { + if (ctx->debug) { + fprintf(stderr, + "ERROR Too big file record number (%d > %d)\n", + sub_addr, MODBUS_MAX_FILE_RECORD_NUMBER); + } + errno = EMBMDATA; + return -1; + } + + req_length = ctx->backend->build_request_basis(ctx, + MODBUS_FC_WRITE_FILE_RECORD, + 0, 0, req); + + byte_count = nb * 2; + req_length -= 4; + req[req_length++] = 7 + byte_count; // one request header + bytes to write + req[req_length++] = 6; + req[req_length++] = addr >> 8; + req[req_length++] = addr & 0x00ff; + req[req_length++] = sub_addr >> 8; + req[req_length++] = sub_addr & 0x00ff; + req[req_length++] = nb >> 8; + req[req_length++] = nb & 0x00ff; + // pack supplied data into request + for (i = 0; i < nb; i++) { + req[req_length++] = src[i] >> 8; + req[req_length++] = src[i] & 0x00FF; + } + + rc = send_msg(ctx, req, req_length); + 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); + } + + return rc; +} + + int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask) { int rc; diff --git a/src/modbus.h b/src/modbus.h index c63f5ceb4..0a4769432 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -68,6 +68,8 @@ MODBUS_BEGIN_DECLS #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F #define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 #define MODBUS_FC_REPORT_SLAVE_ID 0x11 +#define MODBUS_FC_READ_FILE_RECORD 0x14 +#define MODBUS_FC_WRITE_FILE_RECORD 0x15 #define MODBUS_FC_MASK_WRITE_REGISTER 0x16 #define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 @@ -81,6 +83,11 @@ MODBUS_BEGIN_DECLS #define MODBUS_MAX_READ_BITS 2000 #define MODBUS_MAX_WRITE_BITS 1968 +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 14 page 33) + * Sub-Req. x, Record Number (2 bytes): 1 to 9999 (0x270F) + */ +#define MODBUS_MAX_FILE_RECORD_NUMBER 9999 + /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15) * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D) * (chapter 6 section 12 page 31) @@ -211,6 +218,8 @@ MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value); MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); +MODBUS_API int modbus_write_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, const uint16_t *src); +MODBUS_API int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint16_t *dest); MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, const uint16_t *src, int read_addr, int read_nb,