source: trunk/Simulator/modbus.c@ 14

Last change on this file since 14 was 5, checked in by mansoux, 15 years ago

Ajout librairie Modbus base MacOSX

File size: 70.4 KB
Line 
1/*
2 * Copyright © 2001-2008 Stéphane Raimbault <stephane.raimbault@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 The library is designed to send and receive data from a device that
20 communicate via the Modbus protocol.
21
22 The function names used are inspired by the Modicon Modbus Protocol
23 Reference Guide which can be obtained from Schneider at
24 www.schneiderautomation.com.
25
26 Documentation:
27 http://www.easysw.com/~mike/serial/serial.html
28 http://copyleft.free.fr/wordpress/index.php/libmodbus/
29*/
30
31#include <stdio.h>
32#include <string.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <termios.h>
36#include <sys/time.h>
37#include <unistd.h>
38#include <errno.h>
39#include <limits.h>
40#include <fcntl.h>
41
42/* TCP */
43#include <sys/types.h>
44#include <sys/socket.h>
45#include <sys/ioctl.h>
46#include <netinet/in.h>
47#include <netinet/ip.h>
48#include <netinet/tcp.h>
49#include <arpa/inet.h>
50
51#include "modbus.h"
52
53#define UNKNOWN_ERROR_MSG "Not defined in modbus specification"
54
55/* This structure reduces the number of params in functions and so
56 * optimizes the speed of execution (~ 37%). */
57typedef struct {
58 int slave;
59 int function;
60 int t_id;
61} sft_t;
62
63static const uint8_t NB_TAB_ERROR_MSG = 12;
64static const char *TAB_ERROR_MSG[] = {
65 /* 0x00 */ UNKNOWN_ERROR_MSG,
66 /* 0x01 */ "Illegal function code",
67 /* 0x02 */ "Illegal data address",
68 /* 0x03 */ "Illegal data value",
69 /* 0x04 */ "Slave device or server failure",
70 /* 0x05 */ "Acknowledge",
71 /* 0x06 */ "Slave device or server busy",
72 /* 0x07 */ "Negative acknowledge",
73 /* 0x08 */ "Memory parity error",
74 /* 0x09 */ UNKNOWN_ERROR_MSG,
75 /* 0x0A */ "Gateway path unavailable",
76 /* 0x0B */ "Target device failed to respond"
77};
78
79/* Table of CRC values for high-order byte */
80static uint8_t table_crc_hi[] = {
81 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
82 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
83 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
84 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
85 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
86 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
87 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
88 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
89 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
90 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
91 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
92 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
93 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
94 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
95 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
96 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
97 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
98 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
99 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
100 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
101 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
102 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
103 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
104 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
105 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
106 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
107};
108
109/* Table of CRC values for low-order byte */
110static uint8_t table_crc_lo[] = {
111 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
112 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
113 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
114 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
115 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
116 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
117 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
118 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
119 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
120 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
121 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
122 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
123 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
124 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
125 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
126 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
127 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
128 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
129 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
130 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
131 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
132 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
133 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
134 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
135 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
136 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
137};
138
139/* Treats errors and flush or close connection if necessary */
140static void error_treat(modbus_param_t *mb_param, int code, const char *string)
141{
142 printf("\nERROR %s (%d)\n", string, code);
143
144 if (mb_param->error_handling == FLUSH_OR_RECONNECT_ON_ERROR) {
145 switch (code) {
146 case ILLEGAL_DATA_VALUE:
147 case ILLEGAL_DATA_ADDRESS:
148 case ILLEGAL_FUNCTION:
149 break;
150 default:
151 if (mb_param->type_com == RTU) {
152 tcflush(mb_param->fd, TCIOFLUSH);
153 } else {
154 modbus_close(mb_param);
155 modbus_connect(mb_param);
156 }
157 }
158 }
159}
160
161/* Computes the length of the expected response */
162static unsigned int compute_response_length(modbus_param_t *mb_param,
163 uint8_t *query)
164{
165 int resp_length;
166 int offset;
167
168 offset = mb_param->header_length;
169
170 switch (query[offset + 1]) {
171 case FC_READ_COIL_STATUS:
172 case FC_READ_INPUT_STATUS: {
173 /* Header + nb values (code from force_multiple_coils) */
174 int nb = (query[offset + 4] << 8) | query[offset + 5];
175 resp_length = 3 + (nb / 8) + ((nb % 8) ? 1 : 0);
176 }
177 break;
178 case FC_READ_HOLDING_REGISTERS:
179 case FC_READ_INPUT_REGISTERS:
180 /* Header + 2 * nb values */
181 resp_length = 3 + 2 * (query[offset + 4] << 8 |
182 query[offset + 5]);
183 break;
184 case FC_READ_EXCEPTION_STATUS:
185 resp_length = 4;
186 break;
187 default:
188 resp_length = 6;
189 }
190
191 resp_length += offset + mb_param->checksum_length;
192
193 return resp_length;
194}
195
196/* Builds a RTU query header */
197static int build_query_basis_rtu(int slave, int function,
198 int start_addr, int nb,
199 uint8_t *query)
200{
201 query[0] = slave;
202 query[1] = function;
203 query[2] = start_addr >> 8;
204 query[3] = start_addr & 0x00ff;
205 query[4] = nb >> 8;
206 query[5] = nb & 0x00ff;
207
208 return PRESET_QUERY_LENGTH_RTU;
209}
210
211/* Builds a TCP query header */
212static int build_query_basis_tcp(int slave, int function,
213 int start_addr, int nb,
214 uint8_t *query)
215{
216
217 /* Extract from MODBUS Messaging on TCP/IP Implementation
218 Guide V1.0b (page 23/46):
219 The transaction identifier is used to associate the future
220 response with the request. So, at a time, on a TCP
221 connection, this identifier must be unique.
222 */
223 static uint16_t t_id = 0;
224
225 /* Transaction ID */
226 if (t_id < UINT16_MAX)
227 t_id++;
228 else
229 t_id = 0;
230 query[0] = t_id >> 8;
231 query[1] = t_id & 0x00ff;
232
233 /* Protocol Modbus */
234 query[2] = 0;
235 query[3] = 0;
236
237 /* Length to fix later with set_query_length_tcp (4 and 5) */
238
239 query[6] = slave;
240 query[7] = function;
241 query[8] = start_addr >> 8;
242 query[9] = start_addr & 0x00ff;
243 query[10] = nb >> 8;
244 query[11] = nb & 0x00ff;
245
246 return PRESET_QUERY_LENGTH_TCP;
247}
248
249static int build_query_basis(modbus_param_t *mb_param, int slave,
250 int function, int start_addr,
251 int nb, uint8_t *query)
252{
253 if (mb_param->type_com == RTU)
254 return build_query_basis_rtu(slave, function, start_addr,
255 nb, query);
256 else
257 return build_query_basis_tcp(slave, function, start_addr,
258 nb, query);
259}
260
261/* Builds a RTU response header */
262static int build_response_basis_rtu(sft_t *sft, uint8_t *response)
263{
264 response[0] = sft->slave;
265 response[1] = sft->function;
266
267 return PRESET_RESPONSE_LENGTH_RTU;
268}
269
270/* Builds a TCP response header */
271static int build_response_basis_tcp(sft_t *sft, uint8_t *response)
272{
273 /* Extract from MODBUS Messaging on TCP/IP Implementation
274 Guide V1.0b (page 23/46):
275 The transaction identifier is used to associate the future
276 response with the request. */
277 response[0] = sft->t_id >> 8;
278 response[1] = sft->t_id & 0x00ff;
279
280 /* Protocol Modbus */
281 response[2] = 0;
282 response[3] = 0;
283
284 /* Length to fix later with set_message_length_tcp (4 and 5) */
285
286 response[6] = sft->slave;
287 response[7] = sft->function;
288
289 return PRESET_RESPONSE_LENGTH_TCP;
290}
291
292static int build_response_basis(modbus_param_t *mb_param, sft_t *sft,
293 uint8_t *response)
294{
295 if (mb_param->type_com == RTU)
296 return build_response_basis_rtu(sft, response);
297 else
298 return build_response_basis_tcp(sft, response);
299}
300
301/* Sets the length of TCP message in the message (query and response) */
302void set_message_length_tcp(uint8_t *msg, int msg_length)
303{
304 /* Substract the header length to the message length */
305 int mbap_length = msg_length - 6;
306
307 msg[4] = mbap_length >> 8;
308 msg[5] = mbap_length & 0x00FF;
309}
310
311/* Fast CRC */
312static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
313{
314 uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
315 uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
316 unsigned int i; /* will index into CRC lookup */
317
318 /* pass through message buffer */
319 while (buffer_length--) {
320 i = crc_hi ^ *buffer++; /* calculate the CRC */
321 crc_hi = crc_lo ^ table_crc_hi[i];
322 crc_lo = table_crc_lo[i];
323 }
324
325 return (crc_hi << 8 | crc_lo);
326}
327
328/* If CRC is correct returns 0 else returns INVALID_CRC */
329static int check_crc16(modbus_param_t *mb_param,
330 uint8_t *msg,
331 const int msg_length)
332{
333 int ret;
334 uint16_t crc_calc;
335 uint16_t crc_received;
336
337 crc_calc = crc16(msg, msg_length - 2);
338 crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
339
340 /* Check CRC of msg */
341 if (crc_calc == crc_received) {
342 ret = 0;
343 } else {
344 char s_error[64];
345 sprintf(s_error,
346 "invalid crc received %0X - crc_calc %0X",
347 crc_received, crc_calc);
348 ret = INVALID_CRC;
349 error_treat(mb_param, ret, s_error);
350 }
351
352 return ret;
353}
354
355/* Sends a query/response over a serial or a TCP communication */
356static int modbus_send(modbus_param_t *mb_param, uint8_t *query,
357 int query_length)
358{
359 int ret;
360 uint16_t s_crc;
361 int i;
362
363 if (mb_param->type_com == RTU) {
364 s_crc = crc16(query, query_length);
365 query[query_length++] = s_crc >> 8;
366 query[query_length++] = s_crc & 0x00FF;
367 } else {
368 set_message_length_tcp(query, query_length);
369 }
370
371 if (mb_param->debug) {
372 for (i = 0; i < query_length; i++)
373 printf("[%.2X]", query[i]);
374 printf("\n");
375 }
376
377 if (mb_param->saveRawData)
378 {
379 for (i = 0; i < query_length; i++)
380 mb_param->rawQuery[i]=query[i];
381 mb_param->rawQueryLength=query_length;
382 }
383
384 if (mb_param->type_com == RTU)
385 ret = write(mb_param->fd, query, query_length);
386 else
387 ret = send(mb_param->fd, query, query_length, 0);
388
389 /* Return the number of bytes written (0 to n)
390 or PORT_SOCKET_FAILURE on error */
391 if ((ret == -1) || (ret != query_length)) {
392 ret = PORT_SOCKET_FAILURE;
393 error_treat(mb_param, ret, "Write port/socket failure");
394 }
395
396 return ret;
397}
398
399/* Computes the length of the header following the function code */
400static uint8_t compute_query_length_header(int function)
401{
402 int length;
403
404 if (function <= FC_FORCE_SINGLE_COIL ||
405 function == FC_PRESET_SINGLE_REGISTER)
406 /* Read and single write */
407 length = 4;
408 else if (function == FC_FORCE_MULTIPLE_COILS ||
409 function == FC_PRESET_MULTIPLE_REGISTERS)
410 /* Multiple write */
411 length = 5;
412 else
413 length = 0;
414
415 return length;
416}
417
418/* Computes the length of the data to write in the query */
419static int compute_query_length_data(modbus_param_t *mb_param, uint8_t *msg)
420{
421 int function = msg[mb_param->header_length + 1];
422 int length;
423
424 if (function == FC_FORCE_MULTIPLE_COILS ||
425 function == FC_PRESET_MULTIPLE_REGISTERS)
426 length = msg[mb_param->header_length + 6];
427 else
428 length = 0;
429
430 length += mb_param->checksum_length;
431
432 return length;
433}
434
435#define WAIT_DATA() \
436{ \
437 while ((select_ret = select(mb_param->fd+1, &rfds, NULL, NULL, &tv)) == -1) { \
438 if (errno == EINTR) { \
439 printf("A non blocked signal was caught\n"); \
440 /* Necessary after an error */ \
441 FD_ZERO(&rfds); \
442 FD_SET(mb_param->fd, &rfds); \
443 } else { \
444 error_treat(mb_param, SELECT_FAILURE, "Select failure"); \
445 return SELECT_FAILURE; \
446 } \
447 } \
448 \
449 if (select_ret == 0) { \
450 /* Call to error_treat is done later to manage exceptions */ \
451 return COMM_TIME_OUT; \
452 } \
453}
454
455/* Waits a reply from a modbus slave or a query from a modbus master.
456 This function blocks for timeout seconds if there is no reply.
457
458 In
459 - msg_length_computed must be set to MSG_LENGTH_UNDEFINED if undefined
460
461 Out
462 - msg is an array of uint8_t to receive the message
463 - p_msg_length, the variable is assigned to the number of
464 characters received. This value won't be greater than
465 msg_length_computed.
466
467 Returns 0 in success or a negative value if an error occured.
468*/
469static int receive_msg(modbus_param_t *mb_param,
470 int msg_length_computed,
471 uint8_t *msg, int *p_msg_length)
472{
473 int select_ret;
474 int read_ret;
475 fd_set rfds;
476 struct timeval tv;
477 int length_to_read;
478 uint8_t *p_msg;
479 enum { FUNCTION, BYTE, COMPLETE };
480 int state;
481
482 if (mb_param->debug) {
483 if (msg_length_computed == MSG_LENGTH_UNDEFINED)
484 printf("Waiting for a message...\n");
485 else
486 printf("Waiting for a message (%d bytes)...\n",
487 msg_length_computed);
488 }
489
490 /* Add a file descriptor to the set */
491 FD_ZERO(&rfds);
492 FD_SET(mb_param->fd, &rfds);
493
494 if (msg_length_computed == MSG_LENGTH_UNDEFINED) {
495 /* Wait for a message */
496 tv.tv_sec = 60;
497 tv.tv_usec = 0;
498
499 /* The message length is undefined (query receiving) so
500 * we need to analyse the message step by step.
501 * At the first step, we want to reach the function
502 * code because all packets have that information. */
503 msg_length_computed = mb_param->header_length + 2;
504 state = FUNCTION;
505 } else {
506 tv.tv_sec = 0;
507 tv.tv_usec = TIME_OUT_BEGIN_OF_TRAME;
508 state = COMPLETE;
509 }
510
511 length_to_read = msg_length_computed;
512
513 select_ret = 0;
514 WAIT_DATA();
515
516 /* Initialize the readin the message */
517 (*p_msg_length) = 0;
518 p_msg = msg;
519
520 while (select_ret) {
521 if (mb_param->type_com == RTU)
522 read_ret = read(mb_param->fd, p_msg, length_to_read);
523 else
524 read_ret = recv(mb_param->fd, p_msg, length_to_read, 0);
525
526 if (read_ret == 0) {
527 printf("Connection closed\n");
528 return CONNECTION_CLOSED;
529 } else if (read_ret < 0) {
530 /* The only negative possible value is -1 */
531 error_treat(mb_param, PORT_SOCKET_FAILURE,
532 "Read port/socket failure");
533 return PORT_SOCKET_FAILURE;
534 }
535
536 /* Sums bytes received */
537 (*p_msg_length) += read_ret;
538
539 /* Display the hex code of each character received */
540 if (mb_param->debug) {
541 int i;
542 for (i=0; i < read_ret; i++)
543 printf("<%.2X>", p_msg[i]);
544 }
545
546 if ((*p_msg_length) < msg_length_computed) {
547 /* Message incomplete */
548 length_to_read = msg_length_computed - (*p_msg_length);
549 } else {
550 switch (state) {
551 case FUNCTION:
552 /* Function code position */
553 length_to_read = compute_query_length_header(msg[mb_param->header_length + 1]);
554 msg_length_computed += length_to_read;
555 /* It's useless to check
556 p_msg_length_computed value in this
557 case (only defined values are used). */
558 state = BYTE;
559 break;
560 case BYTE:
561 length_to_read = compute_query_length_data(mb_param, msg);
562 msg_length_computed += length_to_read;
563 if (msg_length_computed > MAX_MESSAGE_LENGTH) {
564 error_treat(mb_param, TOO_MANY_DATA, "Too many data");
565 return TOO_MANY_DATA;
566 }
567 state = COMPLETE;
568 break;
569 case COMPLETE:
570 length_to_read = 0;
571 break;
572 }
573 }
574
575 /* Moves the pointer to receive other data */
576 p_msg = &(p_msg[read_ret]);
577
578 if (length_to_read > 0) {
579 /* If no character at the buffer wait
580 TIME_OUT_END_OF_TRAME before to generate an error. */
581 tv.tv_sec = 0;
582 tv.tv_usec = TIME_OUT_END_OF_TRAME;
583
584 WAIT_DATA();
585 } else {
586 /* All chars are received */
587 select_ret = FALSE;
588 }
589 }
590
591 if (mb_param->debug)
592 printf("\n");
593
594 if (mb_param->type_com == RTU) {
595 check_crc16(mb_param, msg, (*p_msg_length));
596 }
597
598 /* OK */
599 return 0;
600}
601
602
603/* Receives the response and checks values (and checksum in RTU).
604
605 Returns:
606 - the number of values (bits or word) if success or the response
607 length if no value is returned
608 - less than 0 for exception errors
609
610 Note: all functions used to send or receive data with modbus return
611 these values. */
612static int modbus_receive(modbus_param_t *mb_param,
613 uint8_t *query,
614 uint8_t *response)
615{
616 int ret, i;
617 int response_length;
618 int response_length_computed;
619 int offset = mb_param->header_length;
620
621 response_length_computed = compute_response_length(mb_param, query);
622 ret = receive_msg(mb_param, response_length_computed,
623 response, &response_length);
624 if (ret == 0) {
625 /* GOOD RESPONSE */
626 int query_nb_value;
627 int response_nb_value;
628
629 /* The number of values is returned if it's corresponding
630 * to the query */
631 switch (response[offset + 1]) {
632 case FC_READ_COIL_STATUS:
633 case FC_READ_INPUT_STATUS:
634 /* Read functions, 8 values in a byte (nb
635 * of values in the query and byte count in
636 * the response. */
637 query_nb_value = (query[offset+4] << 8) + query[offset+5];
638 query_nb_value = (query_nb_value / 8) + ((query_nb_value % 8) ? 1 : 0);
639 response_nb_value = response[offset + 2];
640 break;
641 case FC_READ_HOLDING_REGISTERS:
642 case FC_READ_INPUT_REGISTERS:
643 /* Read functions 1 value = 2 bytes */
644 query_nb_value = (query[offset+4] << 8) + query[offset+5];
645 response_nb_value = (response[offset + 2] / 2);
646 break;
647 case FC_FORCE_MULTIPLE_COILS:
648 case FC_PRESET_MULTIPLE_REGISTERS:
649 /* N Write functions */
650 query_nb_value = (query[offset+4] << 8) + query[offset+5];
651 response_nb_value = (response[offset + 4] << 8) | response[offset + 5];
652 break;
653 case FC_REPORT_SLAVE_ID:
654 /* Report slave ID (bytes received) */
655 query_nb_value = response_nb_value = response_length;
656 break;
657 default:
658 /* 1 Write functions & others */
659 query_nb_value = response_nb_value = 1;
660 }
661
662 if (query_nb_value == response_nb_value)
663 {
664 ret = response_nb_value;
665 if (mb_param->saveRawData)
666 {
667 for (i = 0; i < response_length_computed; i++)
668 mb_param->rawResponse[i]=response[i];
669 mb_param->rawResponseLength=response_length_computed;
670 }
671
672 } else {
673 char *s_error = malloc(64 * sizeof(char));
674 sprintf(s_error, "Quantity (%d) not corresponding to the query (%d)",
675 response_nb_value, query_nb_value);
676 ret = ILLEGAL_DATA_VALUE;
677 error_treat(mb_param, ILLEGAL_DATA_VALUE, s_error);
678 free(s_error);
679 }
680 } else if (ret == COMM_TIME_OUT) {
681
682 if (response_length == (offset + 3 + mb_param->checksum_length)) {
683 /* EXCEPTION CODE RECEIVED */
684
685 /* Optimization allowed because exception response is
686 the smallest trame in modbus protocol (3) so always
687 raise a timeout error */
688
689 /* CRC must be checked here (not done in receive_msg) */
690 if (mb_param->type_com == RTU) {
691 ret = check_crc16(mb_param, response, response_length);
692 if (ret != 0)
693 return ret;
694 }
695
696 /* Check for exception response.
697 0x80 + function is stored in the exception
698 response. */
699 if (0x80 + query[offset + 1] == response[offset + 1]) {
700
701 int exception_code = response[offset + 2];
702 // FIXME check test
703 if (exception_code < NB_TAB_ERROR_MSG) {
704 error_treat(mb_param, -exception_code,
705 TAB_ERROR_MSG[response[offset + 2]]);
706 /* RETURN THE EXCEPTION CODE */
707 /* Modbus error code is negative */
708 return -exception_code;
709 } else {
710 /* The chances are low to hit this
711 case but it can avoid a vicious
712 segfault */
713 char *s_error = malloc(64 * sizeof(char));
714 sprintf(s_error,
715 "Invalid exception code %d",
716 response[offset + 2]);
717 error_treat(mb_param, INVALID_EXCEPTION_CODE,
718 s_error);
719 free(s_error);
720 return INVALID_EXCEPTION_CODE;
721 }
722 }
723 /* If doesn't return previously, return as
724 TIME OUT here */
725 }
726
727 /* COMMUNICATION TIME OUT */
728 error_treat(mb_param, ret, "Communication time out");
729 return ret;
730 }
731
732 return ret;
733}
734
735static int response_io_status(int address, int nb,
736 uint8_t *tab_io_status,
737 uint8_t *response, int offset)
738{
739 int shift = 0;
740 int byte = 0;
741 int i;
742
743 for (i = address; i < address+nb; i++) {
744 byte |= tab_io_status[i] << shift;
745 if (shift == 7) {
746 /* Byte is full */
747 response[offset++] = byte;
748 byte = shift = 0;
749 } else {
750 shift++;
751 }
752 }
753
754 if (shift != 0)
755 response[offset++] = byte;
756
757 return offset;
758}
759
760/* Build the exception response */
761static int response_exception(modbus_param_t *mb_param, sft_t *sft,
762 int exception_code, uint8_t *response)
763{
764 int response_length;
765
766 sft->function = sft->function + 0x80;
767 response_length = build_response_basis(mb_param, sft, response);
768
769 /* Positive exception code */
770 response[response_length++] = -exception_code;
771
772 return response_length;
773}
774
775/* Manages the received query.
776 Analyses the query and constructs a response.
777 If an error occurs, this function construct the response
778 accordingly.
779*/
780void modbus_manage_query(modbus_param_t *mb_param, const uint8_t *query,
781 int query_length, modbus_mapping_t *mb_mapping)
782{
783 int offset = mb_param->header_length;
784 int slave = query[offset];
785 int function = query[offset+1];
786 uint16_t address = (query[offset+2] << 8) + query[offset+3];
787 uint8_t response[MAX_MESSAGE_LENGTH];
788 int resp_length = 0;
789 sft_t sft;
790
791 sft.slave = slave;
792 sft.function = function;
793 if (mb_param->type_com == TCP)
794 sft.t_id = (query[0] << 8) + query[1];
795 else
796 sft.t_id = 0;
797
798 switch (function) {
799 case FC_READ_COIL_STATUS: {
800 int nb = (query[offset+4] << 8) + query[offset+5];
801
802 if ((address + nb) > mb_mapping->nb_coil_status) {
803 printf("Illegal data address %0X in read_coil_status\n",
804 address + nb);
805 resp_length = response_exception(mb_param, &sft,
806 ILLEGAL_DATA_ADDRESS, response);
807 } else {
808 resp_length = build_response_basis(mb_param, &sft, response);
809 response[resp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
810 resp_length = response_io_status(address, nb,
811 mb_mapping->tab_coil_status,
812 response, resp_length);
813 }
814 }
815 break;
816 case FC_READ_INPUT_STATUS: {
817 /* Similar to coil status (but too much arguments to use a
818 * function) */
819 int nb = (query[offset+4] << 8) + query[offset+5];
820
821 if ((address + nb) > mb_mapping->nb_input_status) {
822 printf("Illegal data address %0X in read_input_status\n",
823 address + nb);
824 resp_length = response_exception(mb_param, &sft,
825 ILLEGAL_DATA_ADDRESS, response);
826 } else {
827 resp_length = build_response_basis(mb_param, &sft, response);
828 response[resp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
829 resp_length = response_io_status(address, nb,
830 mb_mapping->tab_input_status,
831 response, resp_length);
832 }
833 }
834 break;
835 case FC_READ_HOLDING_REGISTERS: {
836 int nb = (query[offset+4] << 8) + query[offset+5];
837
838 if ((address + nb) > mb_mapping->nb_holding_registers) {
839 printf("Illegal data address %0X in read_holding_registers\n",
840 address + nb);
841 resp_length = response_exception(mb_param, &sft,
842 ILLEGAL_DATA_ADDRESS, response);
843 } else {
844 int i;
845
846 resp_length = build_response_basis(mb_param, &sft, response);
847 response[resp_length++] = nb << 1;
848 for (i = address; i < address + nb; i++) {
849 response[resp_length++] = mb_mapping->tab_holding_registers[i] >> 8;
850 response[resp_length++] = mb_mapping->tab_holding_registers[i] & 0xFF;
851 }
852 }
853 }
854 break;
855 case FC_READ_INPUT_REGISTERS: {
856 /* Similar to holding registers (but too much arguments to use a
857 * function) */
858 int nb = (query[offset+4] << 8) + query[offset+5];
859
860 if ((address + nb) > mb_mapping->nb_input_registers) {
861 printf("Illegal data address %0X in read_input_registers\n",
862 address + nb);
863 resp_length = response_exception(mb_param, &sft,
864 ILLEGAL_DATA_ADDRESS, response);
865 } else {
866 int i;
867
868 resp_length = build_response_basis(mb_param, &sft, response);
869 response[resp_length++] = nb << 1;
870 for (i = address; i < address + nb; i++) {
871 response[resp_length++] = mb_mapping->tab_input_registers[i] >> 8;
872 response[resp_length++] = mb_mapping->tab_input_registers[i] & 0xFF;
873 }
874 }
875 }
876 break;
877 case FC_FORCE_SINGLE_COIL:
878 if (address >= mb_mapping->nb_coil_status) {
879 printf("Illegal data address %0X in force_singe_coil\n", address);
880 resp_length = response_exception(mb_param, &sft,
881 ILLEGAL_DATA_ADDRESS, response);
882 } else {
883 int data = (query[offset+4] << 8) + query[offset+5];
884
885 if (data == 0xFF00 || data == 0x0) {
886 mb_mapping->tab_coil_status[address] = (data) ? ON : OFF;
887
888 /* In RTU mode, the CRC is computed
889 and added to the query by modbus_send */
890 memcpy(response, query, query_length - mb_param->checksum_length);
891 resp_length = query_length;
892 } else {
893 printf("Illegal data value %0X in force_single_coil request at address %0X\n",
894 data, address);
895 resp_length = response_exception(mb_param, &sft,
896 ILLEGAL_DATA_VALUE, response);
897 }
898 }
899 break;
900 case FC_PRESET_SINGLE_REGISTER:
901 if (address >= mb_mapping->nb_holding_registers) {
902 printf("Illegal data address %0X in preset_holding_register\n", address);
903 resp_length = response_exception(mb_param, &sft,
904 ILLEGAL_DATA_ADDRESS, response);
905 } else {
906 int data = (query[offset+4] << 8) + query[offset+5];
907
908 mb_mapping->tab_holding_registers[address] = data;
909 memcpy(response, query, query_length - mb_param->checksum_length);
910 resp_length = query_length;
911 }
912 break;
913 case FC_FORCE_MULTIPLE_COILS: {
914 int nb = (query[offset+4] << 8) + query[offset+5];
915
916 if ((address + nb) > mb_mapping->nb_coil_status) {
917 printf("Illegal data address %0X in force_multiple_coils\n",
918 address + nb);
919 resp_length = response_exception(mb_param, &sft,
920 ILLEGAL_DATA_ADDRESS, response);
921 } else {
922 /* 6 = byte count, 7 = first byte of data */
923 set_bits_from_bytes(mb_mapping->tab_coil_status, address, nb, &query[offset + 7]);
924
925 resp_length = build_response_basis(mb_param, &sft, response);
926 /* 4 to copy the coil address (2) and the quantity of coils */
927 memcpy(response + resp_length, query + resp_length, 4);
928 resp_length += 4;
929 }
930 }
931 break;
932 case FC_PRESET_MULTIPLE_REGISTERS: {
933 int nb = (query[offset+4] << 8) + query[offset+5];
934
935 if ((address + nb) > mb_mapping->nb_holding_registers) {
936 printf("Illegal data address %0X in preset_multiple_registers\n",
937 address + nb);
938 resp_length = response_exception(mb_param, &sft,
939 ILLEGAL_DATA_ADDRESS, response);
940 } else {
941 int i, j;
942 for (i = address, j = 0; i < address + nb; i++, j += 2) {
943 /* 6 = byte count, 7 and 8 = first value */
944 mb_mapping->tab_holding_registers[i] =
945 (query[offset + 7 + j] << 8) + query[offset + 8 + j];
946 }
947
948 resp_length = build_response_basis(mb_param, &sft, response);
949 /* 4 to copy the address (2) and the no. of registers */
950 memcpy(response + resp_length, query + resp_length, 4);
951 resp_length += 4;
952 }
953 }
954 break;
955 case FC_READ_EXCEPTION_STATUS:
956 case FC_REPORT_SLAVE_ID:
957 printf("Not implemented\n");
958 break;
959 }
960
961 modbus_send(mb_param, response, resp_length);
962}
963
964/* Listens any message on a socket or file descriptor.
965 Returns:
966 - 0 if OK, or a negative error number if the request fails
967 - query, message received
968 - query_length, length in bytes of the message */
969int modbus_listen(modbus_param_t *mb_param, uint8_t *query, int *query_length)
970{
971 int ret;
972
973 /* The length of the query to receive isn't known. */
974 ret = receive_msg(mb_param, MSG_LENGTH_UNDEFINED, query, query_length);
975
976 return ret;
977}
978
979/* Reads IO status */
980static int read_io_status(modbus_param_t *mb_param, int slave, int function,
981 int start_addr, int nb, uint8_t *data_dest)
982{
983 int ret;
984 int query_length;
985
986 uint8_t query[MIN_QUERY_LENGTH];
987 uint8_t response[MAX_MESSAGE_LENGTH];
988
989 query_length = build_query_basis(mb_param, slave, function,
990 start_addr, nb, query);
991
992 ret = modbus_send(mb_param, query, query_length);
993 if (ret > 0) {
994 int i, temp, bit;
995 int pos = 0;
996 int processed = 0;
997 int offset;
998 int offset_length;
999
1000 ret = modbus_receive(mb_param, query, response);
1001 if (ret < 0)
1002 return ret;
1003
1004 offset = mb_param->header_length;
1005
1006 offset_length = offset + ret;
1007 mb_param->response_length=offset_length+2;
1008 for (i = offset; i < offset_length; i++) {
1009 /* Shift reg hi_byte to temp */
1010 temp = response[3 + i];
1011
1012 for (bit = 0x01; (bit & 0xff) && (processed < nb);) {
1013 data_dest[pos++] = (temp & bit) ? TRUE : FALSE;
1014 processed++;
1015 bit = bit << 1;
1016 }
1017
1018 }
1019 }
1020
1021 return ret;
1022}
1023
1024/* Reads the boolean status of coils and sets the array elements
1025 in the destination to TRUE or FALSE. */
1026int read_coil_status(modbus_param_t *mb_param, int slave, int start_addr,
1027 int nb, uint8_t *data_dest)
1028{
1029 int status;
1030
1031 if (nb > MAX_STATUS) {
1032 printf("ERROR Too many coils status requested (%d > %d)\n",
1033 nb, MAX_STATUS);
1034 return TOO_MANY_DATA;
1035 }
1036
1037 status = read_io_status(mb_param, slave, FC_READ_COIL_STATUS,
1038 start_addr, nb, data_dest);
1039
1040 if (status > 0)
1041 status = nb;
1042
1043 return status;
1044}
1045
1046
1047/* Same as read_coil_status but reads the slaves input table */
1048int read_input_status(modbus_param_t *mb_param, int slave, int start_addr,
1049 int nb, uint8_t *data_dest)
1050{
1051 int status;
1052
1053 if (nb > MAX_STATUS) {
1054 printf("ERROR Too many input status requested (%d > %d)\n",
1055 nb, MAX_STATUS);
1056 return TOO_MANY_DATA;
1057 }
1058
1059 status = read_io_status(mb_param, slave, FC_READ_INPUT_STATUS,
1060 start_addr, nb, data_dest);
1061
1062 if (status > 0)
1063 status = nb;
1064
1065 return status;
1066}
1067
1068/* Reads the data from a modbus slave and put that data into an array */
1069static int read_registers(modbus_param_t *mb_param, int slave, int function,
1070 int start_addr, int nb, uint16_t *data_dest)
1071{
1072 int ret;
1073 int query_length;
1074 uint8_t query[MIN_QUERY_LENGTH];
1075 uint8_t response[MAX_MESSAGE_LENGTH];
1076
1077 if (nb > MAX_REGISTERS) {
1078 printf("EROOR Too many holding registers requested (%d > %d)\n",
1079 nb, MAX_REGISTERS);
1080 return TOO_MANY_DATA;
1081 }
1082
1083 query_length = build_query_basis(mb_param, slave, function,
1084 start_addr, nb, query);
1085
1086 ret = modbus_send(mb_param, query, query_length);
1087 if (ret > 0) {
1088 int offset;
1089 int i;
1090
1091 ret = modbus_receive(mb_param, query, response);
1092
1093 offset = mb_param->header_length;
1094 mb_param->response_length=offset+ret+3;
1095
1096 /* If ret is negative, the loop is jumped ! */
1097 for (i = 0; i < ret; i++) {
1098 /* shift reg hi_byte to temp OR with lo_byte */
1099 data_dest[i] = (response[offset + 3 + (i << 1)] << 8) |
1100 response[offset + 4 + (i << 1)];
1101 }
1102 }
1103
1104 return ret;
1105}
1106
1107/* Reads the holding registers in a slave and put the data into an
1108 array */
1109int read_holding_registers(modbus_param_t *mb_param, int slave,
1110 int start_addr, int nb, uint16_t *data_dest)
1111{
1112 int status;
1113
1114 if (nb > MAX_REGISTERS) {
1115 printf("ERROR Too many holding registers requested (%d > %d)\n",
1116 nb, MAX_REGISTERS);
1117 return TOO_MANY_DATA;
1118 }
1119
1120 status = read_registers(mb_param, slave, FC_READ_HOLDING_REGISTERS,
1121 start_addr, nb, data_dest);
1122 return status;
1123}
1124
1125/* Reads the input registers in a slave and put the data into
1126 an array */
1127int read_input_registers(modbus_param_t *mb_param, int slave,
1128 int start_addr, int nb, uint16_t *data_dest)
1129{
1130 int status;
1131
1132 if (nb > MAX_REGISTERS) {
1133 printf("ERROR Too many input registers requested (%d > %d)\n",
1134 nb, MAX_REGISTERS);
1135 return TOO_MANY_DATA;
1136 }
1137
1138 status = read_registers(mb_param, slave, FC_READ_INPUT_REGISTERS,
1139 start_addr, nb, data_dest);
1140
1141 return status;
1142}
1143
1144/* Sends a value to a register in a slave.
1145 Used by force_single_coil and preset_single_register */
1146static int set_single(modbus_param_t *mb_param, int slave, int function,
1147 int addr, int value)
1148{
1149 int ret;
1150 int query_length;
1151 uint8_t query[MIN_QUERY_LENGTH];
1152
1153 query_length = build_query_basis(mb_param, slave, function,
1154 addr, value, query);
1155
1156 ret = modbus_send(mb_param, query, query_length);
1157 if (ret > 0) {
1158 /* Used by force_single_coil and
1159 * preset_single_register */
1160 uint8_t response[MIN_QUERY_LENGTH];
1161 ret = modbus_receive(mb_param, query, response);
1162 }
1163
1164 return ret;
1165}
1166
1167/* Turns ON or OFF a single coil in the slave device */
1168int force_single_coil(modbus_param_t *mb_param, int slave,
1169 int coil_addr, int state)
1170{
1171 int status;
1172
1173 if (state)
1174 state = 0xFF00;
1175
1176 status = set_single(mb_param, slave, FC_FORCE_SINGLE_COIL,
1177 coil_addr, state);
1178
1179 return status;
1180}
1181
1182/* Sets a value in one holding register in the slave device */
1183int preset_single_register(modbus_param_t *mb_param, int slave,
1184 int reg_addr, int value)
1185{
1186 int status;
1187
1188 status = set_single(mb_param, slave, FC_PRESET_SINGLE_REGISTER,
1189 reg_addr, value);
1190
1191 return status;
1192}
1193
1194/* Sets/resets the coils in the slave from an array in argument */
1195int force_multiple_coils(modbus_param_t *mb_param, int slave,
1196 int start_addr, int nb,
1197 const uint8_t *data_src)
1198{
1199 int ret;
1200 int i;
1201 int byte_count;
1202 int query_length;
1203 int coil_check = 0;
1204 int pos = 0;
1205
1206 uint8_t query[MAX_MESSAGE_LENGTH];
1207
1208 if (nb > MAX_STATUS) {
1209 printf("ERROR Writing to too many coils (%d > %d)\n",
1210 nb, MAX_STATUS);
1211 return TOO_MANY_DATA;
1212 }
1213
1214 query_length = build_query_basis(mb_param, slave,
1215 FC_FORCE_MULTIPLE_COILS,
1216 start_addr, nb, query);
1217 byte_count = (nb / 8) + ((nb % 8) ? 1 : 0);
1218 query[query_length++] = byte_count;
1219
1220 for (i = 0; i < byte_count; i++) {
1221 int bit;
1222
1223 bit = 0x01;
1224 query[query_length] = 0;
1225
1226 while ((bit & 0xFF) && (coil_check++ < nb)) {
1227 if (data_src[pos++])
1228 query[query_length] |= bit;
1229 else
1230 query[query_length] &=~ bit;
1231
1232 bit = bit << 1;
1233 }
1234 query_length++;
1235 }
1236
1237 ret = modbus_send(mb_param, query, query_length);
1238 if (ret > 0) {
1239 uint8_t response[MAX_MESSAGE_LENGTH];
1240 ret = modbus_receive(mb_param, query, response);
1241 }
1242
1243
1244 return ret;
1245}
1246
1247/* Copies the values in the slave from the array given in argument */
1248int preset_multiple_registers(modbus_param_t *mb_param, int slave,
1249 int start_addr, int nb,
1250 const uint16_t *data_src)
1251{
1252 int ret;
1253 int i;
1254 int query_length;
1255 int byte_count;
1256
1257 uint8_t query[MAX_MESSAGE_LENGTH];
1258
1259 if (nb > MAX_REGISTERS) {
1260 printf("ERROR Trying to write to too many registers (%d > %d)\n",
1261 nb, MAX_REGISTERS);
1262 return TOO_MANY_DATA;
1263 }
1264
1265 query_length = build_query_basis(mb_param, slave,
1266 FC_PRESET_MULTIPLE_REGISTERS,
1267 start_addr, nb, query);
1268 byte_count = nb * 2;
1269 query[query_length++] = byte_count;
1270
1271 for (i = 0; i < nb; i++) {
1272 query[query_length++] = data_src[i] >> 8;
1273 query[query_length++] = data_src[i] & 0x00FF;
1274 }
1275
1276 ret = modbus_send(mb_param, query, query_length);
1277 if (ret > 0) {
1278 uint8_t response[MAX_MESSAGE_LENGTH];
1279 ret = modbus_receive(mb_param, query, response);
1280 }
1281
1282 return ret;
1283}
1284
1285/* Returns the slave id! */
1286int report_slave_id(modbus_param_t *mb_param, int slave,
1287 uint8_t *data_dest)
1288{
1289 int ret;
1290 int query_length;
1291 uint8_t query[MIN_QUERY_LENGTH];
1292
1293 query_length = build_query_basis(mb_param, slave, FC_REPORT_SLAVE_ID,
1294 0, 0, query);
1295
1296 /* HACKISH, start_addr and count are not used */
1297 query_length -= 4;
1298
1299 ret = modbus_send(mb_param, query, query_length);
1300 if (ret > 0) {
1301 int i;
1302 int offset;
1303 int offset_length;
1304 uint8_t response[MAX_MESSAGE_LENGTH];
1305
1306 /* Byte count, slave id, run indicator status,
1307 additional data */
1308 ret = modbus_receive(mb_param, query, response);
1309 if (ret < 0)
1310 return ret;
1311
1312 offset = mb_param->header_length;
1313 offset_length = offset + ret;
1314
1315 for (i = offset; i < offset_length; i++)
1316 data_dest[i] = response[i];
1317 }
1318
1319 return ret;
1320}
1321
1322/* Initializes the modbus_param_t structure for RTU
1323 - device: "/dev/ttyS0"
1324 - baud: 9600, 19200, 57600, 115200, etc
1325 - parity: "even", "odd" or "none"
1326 - data_bits: 5, 6, 7, 8
1327 - stop_bits: 1, 2
1328*/
1329void modbus_init_rtu(modbus_param_t *mb_param, const char *device,
1330 int baud, const char *parity, int data_bit,
1331 int stop_bit)
1332{
1333 memset(mb_param, 0, sizeof(modbus_param_t));
1334 strcpy(mb_param->device, device);
1335 mb_param->baud = baud;
1336 strcpy(mb_param->parity, parity);
1337 mb_param->debug = FALSE;
1338 mb_param->data_bit = data_bit;
1339 mb_param->stop_bit = stop_bit;
1340 mb_param->type_com = RTU;
1341 mb_param->header_length = HEADER_LENGTH_RTU;
1342 mb_param->checksum_length = CHECKSUM_LENGTH_RTU;
1343 mb_param->saveRawData=FALSE;
1344}
1345
1346/* Initializes the modbus_param_t structure for TCP.
1347 - ip : "192.168.0.5"
1348 - port : 1099
1349
1350 Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one
1351 (502). It's convenient to use a port number greater than or equal
1352 to 1024 because it's not necessary to be root to use this port
1353 number.
1354*/
1355void modbus_init_tcp(modbus_param_t *mb_param, const char *ip, int port)
1356{
1357 memset(mb_param, 0, sizeof(modbus_param_t));
1358 strncpy(mb_param->ip, ip, sizeof(char)*16);
1359 mb_param->port = port;
1360 mb_param->type_com = TCP;
1361 mb_param->header_length = HEADER_LENGTH_TCP;
1362 mb_param->checksum_length = CHECKSUM_LENGTH_TCP;
1363 mb_param->error_handling = FLUSH_OR_RECONNECT_ON_ERROR;
1364 mb_param->saveRawData=FALSE;
1365}
1366
1367/* By default, the error handling mode used is FLUSH_OR_RECONNECT_ON_ERROR.
1368
1369 With FLUSH_OR_RECONNECT_ON_ERROR, the library will flush to I/O
1370 port in RTU mode or attempt an immediate reconnection which may
1371 hang for several seconds if the network to the remote target unit
1372 is down in TCP mode.
1373
1374 With NOP_ON_ERROR, it is expected that the application will
1375 check for error returns and deal with them as necessary.
1376*/
1377void modbus_set_error_handling(modbus_param_t *mb_param,
1378 error_handling_t error_handling)
1379{
1380 if (error_handling == FLUSH_OR_RECONNECT_ON_ERROR ||
1381 error_handling == NOP_ON_ERROR) {
1382 mb_param->error_handling = error_handling;
1383 } else {
1384 printf("Invalid setting for error handling (not changed)\n");
1385 }
1386}
1387
1388
1389/* Sets up a serial port for RTU communications */
1390static int modbus_connect_rtu(modbus_param_t *mb_param)
1391{
1392 struct termios tios;
1393 speed_t speed;
1394
1395 if (mb_param->debug) {
1396 printf("Opening %s at %d bauds (%s)\n",
1397 mb_param->device, mb_param->baud, mb_param->parity);
1398 }
1399
1400 /* The O_NOCTTY flag tells UNIX that this program doesn't want
1401 to be the "controlling terminal" for that port. If you
1402 don't specify this then any input (such as keyboard abort
1403 signals and so forth) will affect your process
1404
1405 Timeouts are ignored in canonical input mode or when the
1406 NDELAY option is set on the file via open or fcntl */
1407 mb_param->fd = open(mb_param->device, O_RDWR | O_NOCTTY | O_NDELAY);
1408 if (mb_param->fd < 0) {
1409 perror("open");
1410 printf("ERROR Can't open the device %s (errno %d)\n",
1411 mb_param->device, errno);
1412 return -1;
1413 }
1414
1415 /* Save */
1416 tcgetattr(mb_param->fd, &(mb_param->old_tios));
1417
1418 memset(&tios, 0, sizeof(struct termios));
1419
1420 /* C_ISPEED Input baud (new interface)
1421 C_OSPEED Output baud (new interface)
1422 */
1423 switch (mb_param->baud) {
1424 case 110:
1425 speed = B110;
1426 break;
1427 case 300:
1428 speed = B300;
1429 break;
1430 case 600:
1431 speed = B600;
1432 break;
1433 case 1200:
1434 speed = B1200;
1435 break;
1436 case 2400:
1437 speed = B2400;
1438 break;
1439 case 4800:
1440 speed = B4800;
1441 break;
1442 case 9600:
1443 speed = B9600;
1444 break;
1445 case 19200:
1446 speed = B19200;
1447 break;
1448 case 38400:
1449 speed = B38400;
1450 break;
1451 case 57600:
1452 speed = B57600;
1453 break;
1454 case 115200:
1455 speed = B115200;
1456 break;
1457 default:
1458 speed = B9600;
1459 printf("WARNING Unknown baud rate %d for %s (B9600 used)\n",
1460 mb_param->baud, mb_param->device);
1461 }
1462
1463 /* Set the baud rate */
1464 if ((cfsetispeed(&tios, speed) < 0) ||
1465 (cfsetospeed(&tios, speed) < 0)) {
1466 perror("cfsetispeed/cfsetospeed\n");
1467 return -1;
1468 }
1469
1470 /* C_CFLAG Control options
1471 CLOCAL Local line - do not change "owner" of port
1472 CREAD Enable receiver
1473 */
1474 tios.c_cflag |= (CREAD | CLOCAL);
1475 /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
1476
1477 /* Set data bits (5, 6, 7, 8 bits)
1478 CSIZE Bit mask for data bits
1479 */
1480 tios.c_cflag &= ~CSIZE;
1481 switch (mb_param->data_bit) {
1482 case 5:
1483 tios.c_cflag |= CS5;
1484 break;
1485 case 6:
1486 tios.c_cflag |= CS6;
1487 break;
1488 case 7:
1489 tios.c_cflag |= CS7;
1490 break;
1491 case 8:
1492 default:
1493 tios.c_cflag |= CS8;
1494 break;
1495 }
1496
1497 /* Stop bit (1 or 2) */
1498 if (mb_param->stop_bit == 1)
1499 tios.c_cflag &=~ CSTOPB;
1500 else /* 2 */
1501 tios.c_cflag |= CSTOPB;
1502
1503 /* PARENB Enable parity bit
1504 PARODD Use odd parity instead of even */
1505 if (strncmp(mb_param->parity, "none", 4) == 0) {
1506 tios.c_cflag &=~ PARENB;
1507 } else if (strncmp(mb_param->parity, "even", 4) == 0) {
1508 tios.c_cflag |= PARENB;
1509 tios.c_cflag &=~ PARODD;
1510 } else {
1511 /* odd */
1512 tios.c_cflag |= PARENB;
1513 tios.c_cflag |= PARODD;
1514 }
1515
1516 /* Read the man page of termios if you need more information. */
1517
1518 /* This field isn't used on POSIX systems
1519 tios.c_line = 0;
1520 */
1521
1522 /* C_LFLAG Line options
1523
1524 ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
1525 ICANON Enable canonical input (else raw)
1526 XCASE Map uppercase \lowercase (obsolete)
1527 ECHO Enable echoing of input characters
1528 ECHOE Echo erase character as BS-SP-BS
1529 ECHOK Echo NL after kill character
1530 ECHONL Echo NL
1531 NOFLSH Disable flushing of input buffers after
1532 interrupt or quit characters
1533 IEXTEN Enable extended functions
1534 ECHOCTL Echo control characters as ^char and delete as ~?
1535 ECHOPRT Echo erased character as character erased
1536 ECHOKE BS-SP-BS entire line on line kill
1537 FLUSHO Output being flushed
1538 PENDIN Retype pending input at next read or input char
1539 TOSTOP Send SIGTTOU for background output
1540
1541 Canonical input is line-oriented. Input characters are put
1542 into a buffer which can be edited interactively by the user
1543 until a CR (carriage return) or LF (line feed) character is
1544 received.
1545
1546 Raw input is unprocessed. Input characters are passed
1547 through exactly as they are received, when they are
1548 received. Generally you'll deselect the ICANON, ECHO,
1549 ECHOE, and ISIG options when using raw input
1550 */
1551
1552 /* Raw input */
1553 tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
1554
1555 /* C_IFLAG Input options
1556
1557 Constant Description
1558 INPCK Enable parity check
1559 IGNPAR Ignore parity errors
1560 PARMRK Mark parity errors
1561 ISTRIP Strip parity bits
1562 IXON Enable software flow control (outgoing)
1563 IXOFF Enable software flow control (incoming)
1564 IXANY Allow any character to start flow again
1565 IGNBRK Ignore break condition
1566 BRKINT Send a SIGINT when a break condition is detected
1567 INLCR Map NL to CR
1568 IGNCR Ignore CR
1569 ICRNL Map CR to NL
1570 IUCLC Map uppercase to lowercase
1571 IMAXBEL Echo BEL on input line too long
1572 */
1573 if (strncmp(mb_param->parity, "none", 4) == 0) {
1574 tios.c_iflag &= ~INPCK;
1575 } else {
1576 tios.c_iflag |= INPCK;
1577 }
1578
1579 /* Software flow control is disabled */
1580 tios.c_iflag &= ~(IXON | IXOFF | IXANY);
1581
1582 /* C_OFLAG Output options
1583 OPOST Postprocess output (not set = raw output)
1584 ONLCR Map NL to CR-NL
1585
1586 ONCLR ant others needs OPOST to be enabled
1587 */
1588
1589 /* Raw ouput */
1590 tios.c_oflag &=~ OPOST;
1591
1592 /* C_CC Control characters
1593 VMIN Minimum number of characters to read
1594 VTIME Time to wait for data (tenths of seconds)
1595
1596 UNIX serial interface drivers provide the ability to
1597 specify character and packet timeouts. Two elements of the
1598 c_cc array are used for timeouts: VMIN and VTIME. Timeouts
1599 are ignored in canonical input mode or when the NDELAY
1600 option is set on the file via open or fcntl.
1601
1602 VMIN specifies the minimum number of characters to read. If
1603 it is set to 0, then the VTIME value specifies the time to
1604 wait for every character read. Note that this does not mean
1605 that a read call for N bytes will wait for N characters to
1606 come in. Rather, the timeout will apply to the first
1607 character and the read call will return the number of
1608 characters immediately available (up to the number you
1609 request).
1610
1611 If VMIN is non-zero, VTIME specifies the time to wait for
1612 the first character read. If a character is read within the
1613 time given, any read will block (wait) until all VMIN
1614 characters are read. That is, once the first character is
1615 read, the serial interface driver expects to receive an
1616 entire packet of characters (VMIN bytes total). If no
1617 character is read within the time allowed, then the call to
1618 read returns 0. This method allows you to tell the serial
1619 driver you need exactly N bytes and any read call will
1620 return 0 or N bytes. However, the timeout only applies to
1621 the first character read, so if for some reason the driver
1622 misses one character inside the N byte packet then the read
1623 call could block forever waiting for additional input
1624 characters.
1625
1626 VTIME specifies the amount of time to wait for incoming
1627 characters in tenths of seconds. If VTIME is set to 0 (the
1628 default), reads will block (wait) indefinitely unless the
1629 NDELAY option is set on the port with open or fcntl.
1630 */
1631 /* Unused because we use open with the NDELAY option */
1632 tios.c_cc[VMIN] = 0;
1633 tios.c_cc[VTIME] = 0;
1634
1635 if (tcsetattr(mb_param->fd, TCSANOW, &tios) < 0) {
1636 perror("tcsetattr\n");
1637 return -1;
1638 }
1639
1640 return 0;
1641}
1642
1643/* Establishes a modbus TCP connection with a modbus slave */
1644static int modbus_connect_tcp(modbus_param_t *mb_param)
1645{
1646 int ret;
1647 int option;
1648 struct sockaddr_in addr;
1649
1650 addr.sin_family = AF_INET;
1651 addr.sin_port = htons(mb_param->port);
1652 addr.sin_addr.s_addr = inet_addr(mb_param->ip);
1653
1654 mb_param->fd = socket(AF_INET, SOCK_STREAM, 0);
1655 if (mb_param->fd < 0) {
1656 return mb_param->fd;
1657 }
1658
1659 /* Set the TCP no delay flag */
1660 /* SOL_TCP = IPPROTO_TCP */
1661 option = 1;
1662 ret = setsockopt(mb_param->fd, IPPROTO_TCP, TCP_NODELAY,
1663 (const void *)&option, sizeof(int));
1664 if (ret < 0) {
1665 perror("setsockopt");
1666 close(mb_param->fd);
1667 return ret;
1668 }
1669
1670#ifndef __APPLE__
1671 /* Set the IP low delay option- Not Supported on Mac OS X MJB 02/16/09 */
1672 option = IPTOS_LOWDELAY;
1673 ret = setsockopt(mb_param->fd, IPPROTO_TCP, IP_TOS,
1674 (const void *)&option, sizeof(int));
1675 if (ret < 0) {
1676 perror("setsockopt");
1677 close(mb_param->fd);
1678 return ret;
1679 }
1680#endif
1681
1682 if (mb_param->debug) {
1683 printf("Connecting to %s\n", mb_param->ip);
1684 }
1685
1686 ret = connect(mb_param->fd, (struct sockaddr *)&addr,
1687 sizeof(struct sockaddr_in));
1688 if (ret < 0) {
1689 perror("connect");
1690 close(mb_param->fd);
1691 return ret;
1692 }
1693
1694 return 0;
1695}
1696
1697/* Establishes a modbus connexion.
1698 Returns -1 if an error occured. */
1699int modbus_connect(modbus_param_t *mb_param)
1700{
1701 int ret;
1702
1703 if (mb_param->type_com == RTU)
1704 ret = modbus_connect_rtu(mb_param);
1705 else
1706 ret = modbus_connect_tcp(mb_param);
1707
1708 return ret;
1709}
1710
1711/* Closes the file descriptor in RTU mode */
1712static void modbus_close_rtu(modbus_param_t *mb_param)
1713{
1714 if (tcsetattr(mb_param->fd, TCSANOW, &(mb_param->old_tios)) < 0)
1715 perror("tcsetattr");
1716
1717 close(mb_param->fd);
1718}
1719
1720/* Closes the network connection and socket in TCP mode */
1721static void modbus_close_tcp(modbus_param_t *mb_param)
1722{
1723 shutdown(mb_param->fd, SHUT_RDWR);
1724 close(mb_param->fd);
1725}
1726
1727/* Closes a modbus connection */
1728void modbus_close(modbus_param_t *mb_param)
1729{
1730 if (mb_param->type_com == RTU)
1731 modbus_close_rtu(mb_param);
1732 else
1733 modbus_close_tcp(mb_param);
1734}
1735
1736/* Activates the debug messages */
1737void modbus_set_debug(modbus_param_t *mb_param, int boolean)
1738{
1739 mb_param->debug = boolean;
1740}
1741
1742
1743// Activates saving raw query and responses
1744void modbus_set_raw_data_save(modbus_param_t *mb_param, int boolean)
1745{
1746 mb_param->saveRawData=boolean;
1747}
1748
1749// Returns a raw query
1750int modbus_last_raw_query(modbus_param_t *mb_param, uint8_t *rawQuery)
1751{
1752 int i;
1753 if(mb_param->rawQuery!=NULL)
1754 {
1755 for (i = 0; i < MIN_QUERY_LENGTH; i++)
1756 rawQuery[i]=mb_param->rawQuery[i];
1757 return TRUE;
1758 }
1759 return FALSE;
1760}
1761
1762// Returns a raw response
1763int modbus_last_raw_response(modbus_param_t *mb_param, uint8_t *rawResponse)
1764{
1765 int i;
1766 if(mb_param->rawResponse!=NULL)
1767 {
1768 for (i = 0; i < mb_param->rawResponseLength; i++)
1769 rawResponse[i]=mb_param->rawResponse[i];
1770 return TRUE;
1771 }
1772 return FALSE;
1773}
1774
1775int modbus_last_raw_query_length(modbus_param_t *mb_param)
1776{
1777 return(mb_param->rawQueryLength);
1778}
1779
1780int modbus_last_raw_response_length(modbus_param_t *mb_param)
1781{
1782 return(mb_param->rawResponseLength);
1783}
1784
1785
1786/* Allocates 4 arrays to store coils, input status, input registers and
1787 holding registers. The pointers are stored in modbus_mapping structure.
1788
1789 Returns: TRUE if ok, FALSE on failure
1790*/
1791int modbus_mapping_new(modbus_mapping_t *mb_mapping,
1792 int nb_coil_status, int nb_input_status,
1793 int nb_holding_registers, int nb_input_registers)
1794{
1795 /* 0X */
1796 mb_mapping->nb_coil_status = nb_coil_status;
1797 mb_mapping->tab_coil_status =
1798 (uint8_t *) malloc(nb_coil_status * sizeof(uint8_t));
1799 memset(mb_mapping->tab_coil_status, 0,
1800 nb_coil_status * sizeof(uint8_t));
1801 if (mb_mapping->tab_coil_status == NULL)
1802 return FALSE;
1803
1804 /* 1X */
1805 mb_mapping->nb_input_status = nb_input_status;
1806 mb_mapping->tab_input_status =
1807 (uint8_t *) malloc(nb_input_status * sizeof(uint8_t));
1808 memset(mb_mapping->tab_input_status, 0,
1809 nb_input_status * sizeof(uint8_t));
1810 if (mb_mapping->tab_input_status == NULL) {
1811 free(mb_mapping->tab_coil_status);
1812 return FALSE;
1813 }
1814
1815 /* 4X */
1816 mb_mapping->nb_holding_registers = nb_holding_registers;
1817 mb_mapping->tab_holding_registers =
1818 (uint16_t *) malloc(nb_holding_registers * sizeof(uint16_t));
1819 memset(mb_mapping->tab_holding_registers, 0,
1820 nb_holding_registers * sizeof(uint16_t));
1821 if (mb_mapping->tab_holding_registers == NULL) {
1822 free(mb_mapping->tab_coil_status);
1823 free(mb_mapping->tab_input_status);
1824 return FALSE;
1825 }
1826
1827 /* 3X */
1828 mb_mapping->nb_input_registers = nb_input_registers;
1829 mb_mapping->tab_input_registers =
1830 (uint16_t *) malloc(nb_input_registers * sizeof(uint16_t));
1831 memset(mb_mapping->tab_input_registers, 0,
1832 nb_input_registers * sizeof(uint16_t));
1833 if (mb_mapping->tab_input_registers == NULL) {
1834 free(mb_mapping->tab_coil_status);
1835 free(mb_mapping->tab_input_status);
1836 free(mb_mapping->tab_holding_registers);
1837 return FALSE;
1838 }
1839
1840 return TRUE;
1841}
1842
1843/* Frees the 4 arrays */
1844void modbus_mapping_free(modbus_mapping_t *mb_mapping)
1845{
1846 free(mb_mapping->tab_coil_status);
1847 free(mb_mapping->tab_input_status);
1848 free(mb_mapping->tab_holding_registers);
1849 free(mb_mapping->tab_input_registers);
1850}
1851
1852/* Listens for any query from a modbus master in TCP */
1853int modbus_init_listen_tcp(modbus_param_t *mb_param)
1854{
1855 int ret;
1856 int new_socket;
1857 struct sockaddr_in addr;
1858 socklen_t addrlen;
1859
1860 addr.sin_family = AF_INET;
1861 /* If the modbus port is < to 1024, we need the setuid root. */
1862 addr.sin_port = htons(mb_param->port);
1863 addr.sin_addr.s_addr = INADDR_ANY;
1864 memset(&(addr.sin_zero), '\0', 8);
1865
1866 new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1867 if (new_socket < 0) {
1868 perror("socket");
1869 exit(1);
1870 } else {
1871 printf("Socket OK\n");
1872 }
1873
1874 ret = bind(new_socket, (struct sockaddr *)&addr,
1875 sizeof(struct sockaddr_in));
1876 if (ret < 0) {
1877 perror("bind");
1878 close(new_socket);
1879 exit(1);
1880 } else {
1881 printf("Bind OK\n");
1882 }
1883
1884 ret = listen(new_socket, 1);
1885 if (ret != 0) {
1886 perror("listen");
1887 close(new_socket);
1888 exit(1);
1889 } else {
1890 printf("Listen OK\n");
1891 }
1892
1893 addrlen = sizeof(struct sockaddr_in);
1894 mb_param->fd = accept(new_socket, (struct sockaddr *)&addr, &addrlen);
1895 if (ret < 0) {
1896 perror("accept");
1897 close(new_socket);
1898 new_socket = 0;
1899 exit(1);
1900 } else {
1901 printf("The client %s is connected\n",
1902 inet_ntoa(addr.sin_addr));
1903 }
1904
1905 return new_socket;
1906}
1907
1908/** Utils **/
1909
1910/* Sets many input/coil status from a single byte value (all 8 bits of
1911 the byte value are setted) */
1912void set_bits_from_byte(uint8_t *dest, int address, const uint8_t value)
1913{
1914 int i;
1915
1916 for (i=0; i<8; i++) {
1917 dest[address+i] = (value & (1 << i)) ? ON : OFF;
1918 }
1919}
1920
1921/* Sets many input/coil status from a table of bytes (only the bits
1922 between address and address + nb_bits are setted) */
1923void set_bits_from_bytes(uint8_t *dest, int address, int nb_bits,
1924 const uint8_t tab_byte[])
1925{
1926 int i;
1927 int shift = 0;
1928
1929 for (i = address; i < address + nb_bits; i++) {
1930 dest[i] = tab_byte[(i - address) / 8] & (1 << shift) ? ON : OFF;
1931 /* gcc doesn't like: shift = (++shift) % 8; */
1932 shift++;
1933 shift %= 8;
1934 }
1935}
1936
1937/* Gets the byte value from many input/coil status.
1938 To obtain a full byte, set nb_bits to 8. */
1939uint8_t get_byte_from_bits(const uint8_t *src, int address, int nb_bits)
1940{
1941 int i;
1942 uint8_t value = 0;
1943
1944 if (nb_bits > 8) {
1945 printf("Error: nb_bits is too big\n");
1946 nb_bits = 8;
1947 }
1948
1949 for (i=0; i < nb_bits; i++) {
1950 value |= (src[address+i] << i);
1951 }
1952
1953 return value;
1954}
Note: See TracBrowser for help on using the repository browser.