Commit 0e4e82e8cefefde1a233ad31529da0fd943b820f
1 parent
b00b27cb
New function modbus_send_raw_request
The _modbus_send_raw_request()_ function shall send a request via the socket of the context 'ctx'. This function must be used for debugging purposes because you have to take care to make a valid request by hand. The function only adds to the message, the header or CRC of the selected backend, so 'raw_req' must start and contain at least a slave/unit identifier and a function code. This function can be used to send request not handled by the library.
Showing
5 changed files
with
128 additions
and
0 deletions
doc/Makefile.am
| @@ -13,6 +13,7 @@ MAN3 = \ | @@ -13,6 +13,7 @@ MAN3 = \ | ||
| 13 | modbus_read_input_bits.3 \ | 13 | modbus_read_input_bits.3 \ |
| 14 | modbus_read_input_registers.3 \ | 14 | modbus_read_input_registers.3 \ |
| 15 | modbus_read_registers.3 \ | 15 | modbus_read_registers.3 \ |
| 16 | + modbus_send_raw_request.3 \ | ||
| 16 | modbus_set_debug.3 \ | 17 | modbus_set_debug.3 \ |
| 17 | modbus_set_error_recovery.3 \ | 18 | modbus_set_error_recovery.3 \ |
| 18 | modbus_set_slave.3 \ | 19 | modbus_set_slave.3 \ |
doc/modbus_send_raw_request.txt
0 → 100644
| 1 | +modbus_send_raw_request(3) | ||
| 2 | +========================== | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +NAME | ||
| 6 | +---- | ||
| 7 | +modbus_send_raw_request - send a raw request | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +SYNOPSIS | ||
| 11 | +-------- | ||
| 12 | +*int modbus_send_raw_request(*modbus_t 'ctx', uint8_t *'raw_req, int 'raw_req_length');* | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +DESCRIPTION | ||
| 16 | +----------- | ||
| 17 | +The _modbus_send_raw_request()_ function shall send a request via the socket of | ||
| 18 | +the context 'ctx'. This function must be used for debugging purposes because you | ||
| 19 | +have to take care to make a valid request by hand. The function only adds to the | ||
| 20 | +message, the header or CRC of the selected backend, so 'raw_req' must start and | ||
| 21 | +contain at least a slave/unit identifier and a function code. This function can | ||
| 22 | +be used to send request not handled by the library. | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +RETURN VALUE | ||
| 26 | +------------ | ||
| 27 | +The _modbus_send_raw_request()_ function shall return the full message length, | ||
| 28 | +counting the extra data relating to the backend, if successful. Otherwise it | ||
| 29 | +shall return -1 and set errno. | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +EXAMPLE | ||
| 33 | +------- | ||
| 34 | +[source,c] | ||
| 35 | +------------------- | ||
| 36 | +modbus_t *ctx; | ||
| 37 | +/* Read 5 holding registers from address 1 */ | ||
| 38 | +uint8_t raw_req[] = { 0xFF, 0x03, 0x00, 0x01, 0x0, 0x05 }; | ||
| 39 | +int req_length; | ||
| 40 | +uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH]; | ||
| 41 | + | ||
| 42 | +ctx = modbus_new_tcp("127.0.0.1", 1502); | ||
| 43 | +if (modbus_connect(ctx) == -1) { | ||
| 44 | + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); | ||
| 45 | + modbus_free(ctx); | ||
| 46 | + return -1; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +req_length = modbus_send_raw_request(ctx, raw_req, 6 * sizeof(uint8_t)); | ||
| 50 | +modbus_receive(ctx, -1, rsp); | ||
| 51 | + | ||
| 52 | +modbus_close(ctx); | ||
| 53 | +modbus_free(ctx); | ||
| 54 | +------------------- | ||
| 55 | + | ||
| 56 | +SEE ALSO | ||
| 57 | +-------- | ||
| 58 | +linkmb:modbus_receive[3] | ||
| 59 | + | ||
| 60 | + | ||
| 61 | +AUTHORS | ||
| 62 | +------- | ||
| 63 | +The libmodbus documentation was written by Stéphane Raimbault | ||
| 64 | +<stephane.raimbault@gmail.com> |
src/modbus.c
| @@ -175,6 +175,34 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) | @@ -175,6 +175,34 @@ static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length) | ||
| 175 | return rc; | 175 | return rc; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | +int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length) | ||
| 179 | +{ | ||
| 180 | + sft_t sft; | ||
| 181 | + uint8_t req[MAX_MESSAGE_LENGTH]; | ||
| 182 | + int req_length; | ||
| 183 | + | ||
| 184 | + if (raw_req_length < 2) { | ||
| 185 | + /* The raw request must contain function and slave at least */ | ||
| 186 | + errno = EINVAL; | ||
| 187 | + return -1; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + sft.slave = raw_req[0]; | ||
| 191 | + sft.function = raw_req[1]; | ||
| 192 | + /* The t_id is left to zero */ | ||
| 193 | + sft.t_id = 0; | ||
| 194 | + /* This response function only set the header so it's convenient here */ | ||
| 195 | + req_length = ctx->backend->build_response_basis(&sft, req); | ||
| 196 | + | ||
| 197 | + if (raw_req_length > 2) { | ||
| 198 | + /* Copy data after function code */ | ||
| 199 | + memcpy(req + req_length, raw_req + 2, raw_req_length - 2); | ||
| 200 | + req_length += raw_req_length - 2; | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + return send_msg(ctx, req, req_length); | ||
| 204 | +} | ||
| 205 | + | ||
| 178 | /* | 206 | /* |
| 179 | ---------- Request Indication ---------- | 207 | ---------- Request Indication ---------- |
| 180 | | Client | ---------------------->| Server | | 208 | | Client | ---------------------->| Server | |
src/modbus.h
| @@ -173,6 +173,8 @@ modbus_mapping_t* modbus_mapping_new(int nb_coil_status, int nb_input_status, | @@ -173,6 +173,8 @@ modbus_mapping_t* modbus_mapping_new(int nb_coil_status, int nb_input_status, | ||
| 173 | int nb_holding_registers, int nb_input_registers); | 173 | int nb_holding_registers, int nb_input_registers); |
| 174 | void modbus_mapping_free(modbus_mapping_t *mb_mapping); | 174 | void modbus_mapping_free(modbus_mapping_t *mb_mapping); |
| 175 | 175 | ||
| 176 | +int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length); | ||
| 177 | + | ||
| 176 | int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req); | 178 | int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req); |
| 177 | int modbus_reply(modbus_t *ctx, const uint8_t *req, | 179 | int modbus_reply(modbus_t *ctx, const uint8_t *req, |
| 178 | int req_length, modbus_mapping_t *mb_mapping); | 180 | int req_length, modbus_mapping_t *mb_mapping); |
tests/unit-test-client.c
| @@ -628,6 +628,39 @@ int main(int argc, char *argv[]) | @@ -628,6 +628,39 @@ int main(int argc, char *argv[]) | ||
| 628 | goto close; | 628 | goto close; |
| 629 | } | 629 | } |
| 630 | 630 | ||
| 631 | + /** RAW REQUEST */ | ||
| 632 | + printf("\nTEST RAW REQUEST:\n"); | ||
| 633 | + { | ||
| 634 | + const int RAW_REQ_LENGTH = 6; | ||
| 635 | + uint8_t raw_req[] = { (use_backend == RTU) ? SERVER_ID : 0xFF, | ||
| 636 | + 0x03, 0x00, 0x01, 0x0, 0x05 }; | ||
| 637 | + int req_length; | ||
| 638 | + uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH]; | ||
| 639 | + | ||
| 640 | + req_length = modbus_send_raw_request(ctx, raw_req, | ||
| 641 | + RAW_REQ_LENGTH * sizeof(uint8_t)); | ||
| 642 | + | ||
| 643 | + if ((use_backend == RTU && req_length == (RAW_REQ_LENGTH + 2)) || | ||
| 644 | + ((use_backend == TCP || use_backend == TCP_PI) && | ||
| 645 | + req_length == (RAW_REQ_LENGTH + 6))) { | ||
| 646 | + printf("OK\n"); | ||
| 647 | + } else { | ||
| 648 | + printf("FAILED (%d)\n", req_length); | ||
| 649 | + goto close; | ||
| 650 | + } | ||
| 651 | + | ||
| 652 | + printf("* modbus_receive: "); | ||
| 653 | + rc = modbus_receive(ctx, -1, rsp); | ||
| 654 | + if ((use_backend == RTU && rc == 15) || | ||
| 655 | + ((use_backend == TCP || use_backend == TCP_PI) && | ||
| 656 | + rc == 19)) { | ||
| 657 | + printf("OK\n"); | ||
| 658 | + } else { | ||
| 659 | + printf("FAILED (%d)\n", rc); | ||
| 660 | + goto close; | ||
| 661 | + } | ||
| 662 | + } | ||
| 663 | + | ||
| 631 | printf("\nALL TESTS PASS WITH SUCCESS.\n"); | 664 | printf("\nALL TESTS PASS WITH SUCCESS.\n"); |
| 632 | 665 | ||
| 633 | close: | 666 | close: |