modbus_client.c
Go to the documentation of this file.
1 /**
2  * @file modbus_client.c
3  * @brief Modbus/TCP client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MODBUS_TRACE_LEVEL
33 
34 //Dependencies
35 #include "modbus/modbus_client.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (MODBUS_CLIENT_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Initialize Modbus/TCP client context
47  * @param[in] context Pointer to the Modbus/TCP client context
48  * @return Error code
49  **/
50 
52 {
53 #if (MODBUS_CLIENT_TLS_SUPPORT == ENABLED)
54  error_t error;
55 #endif
56 
57  //Make sure the Modbus/TCP client context is valid
58  if(context == NULL)
60 
61  //Clear Modbus/TCP client context
62  osMemset(context, 0, sizeof(ModbusClientContext));
63 
64  //Attach TCP/IP stack context
65  context->netContext = netGetDefaultContext();
66 
67 #if (MODBUS_CLIENT_TLS_SUPPORT == ENABLED)
68  //Initialize TLS session state
69  error = tlsInitSessionState(&context->tlsSession);
70  //Any error to report?
71  if(error)
72  return error;
73 #endif
74 
75  //Initialize Modbus/TCP client state
76  context->state = MODBUS_CLIENT_STATE_DISCONNECTED;
77 
78  //Default timeout
79  context->timeout = MODBUS_CLIENT_DEFAULT_TIMEOUT;
80  //Default unit identifier
81  context->unitId = MODBUS_DEFAULT_UNIT_ID;
82 
83  //The transaction identifier is used to uniquely identify the matching
84  //requests and responses
85  context->transactionId = (uint16_t) netGetRand(context->netContext);
86 
87  //Successful initialization
88  return NO_ERROR;
89 }
90 
91 
92 #if (MODBUS_CLIENT_TLS_SUPPORT == ENABLED)
93 
94 /**
95  * @brief Register TLS initialization callback function
96  * @param[in] context Pointer to the Modbus/TCP client context
97  * @param[in] callback TLS initialization callback function
98  * @return Error code
99  **/
100 
103 {
104  //Make sure the Modbus/TCP client context is valid
105  if(context == NULL)
107 
108  //Save callback function
109  context->tlsInitCallback = callback;
110 
111  //Successful processing
112  return NO_ERROR;
113 }
114 
115 #endif
116 
117 
118 /**
119  * @brief Set timeout value for blocking operations
120  * @param[in] context Pointer to the Modbus/TCP client context
121  * @param[in] timeout Timeout value, in milliseconds
122  * @return Error code
123  **/
124 
126 {
127  //Make sure the Modbus/TCP client context is valid
128  if(context == NULL)
130 
131  //Save timeout value
132  context->timeout = timeout;
133 
134  //Successful processing
135  return NO_ERROR;
136 }
137 
138 
139 /**
140  * @brief Set unit identifier
141  * @param[in] context Pointer to the Modbus/TCP client context
142  * @param[in] unitId Identifier of the remote slave
143  * @return Error code
144  **/
145 
147 {
148  //Make sure the Modbus/TCP client context is valid
149  if(context == NULL)
151 
152  //Save unit identifier
153  context->unitId = unitId;
154 
155  //Successful processing
156  return NO_ERROR;
157 }
158 
159 
160 /**
161  * @brief Bind the Modbus/TCP client to a particular network interface
162  * @param[in] context Pointer to the Modbus/TCP client context
163  * @param[in] interface Network interface to be used
164  * @return Error code
165  **/
166 
168  NetInterface *interface)
169 {
170  //Make sure the Modbus/TCP client context is valid
171  if(context == NULL)
173 
174  //Explicitly associate the Modbus/TCP client with the specified interface
175  context->interface = interface;
176 
177  //Successful processing
178  return NO_ERROR;
179 }
180 
181 
182 /**
183  * @brief Establish connection with the Modbus/TCP server
184  * @param[in] context Pointer to the Modbus/TCP client context
185  * @param[in] serverIpAddr IP address of the server to connect to
186  * @param[in] serverPort TCP port number that will be used
187  * @return Error code
188  **/
189 
191  const IpAddr *serverIpAddr, uint16_t serverPort)
192 {
193  error_t error;
194 
195  //Check parameters
196  if(context == NULL || serverIpAddr == NULL)
198 
199  //Initialize status code
200  error = NO_ERROR;
201 
202  //Establish connection with the Modbus/TCP server
203  while(!error)
204  {
205  //Check current state
206  if(context->state == MODBUS_CLIENT_STATE_DISCONNECTED)
207  {
208  //Open network connection
209  error = modbusClientOpenConnection(context);
210 
211  //Check status code
212  if(!error)
213  {
214  //Save current time
215  context->timestamp = osGetSystemTime();
216  //Update Modbus/TCP client state
217  context->state = MODBUS_CLIENT_STATE_CONNECTING;
218  }
219  }
220  else if(context->state == MODBUS_CLIENT_STATE_CONNECTING)
221  {
222  //Establish network connection
223  error = modbusClientEstablishConnection(context, serverIpAddr,
224  serverPort);
225 
226  //Check status code
227  if(error == NO_ERROR)
228  {
229  //Update Modbus/TCP client state
230  context->state = MODBUS_CLIENT_STATE_CONNECTED;
231  }
232  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
233  {
234  //Check whether the timeout has elapsed
235  error = modbusClientCheckTimeout(context);
236  }
237  else
238  {
239  //A communication error has occurred
240  }
241  }
242  else if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
243  {
244  //The Modbus/TCP client is connected
245  break;
246  }
247  else
248  {
249  //Invalid state
250  error = ERROR_WRONG_STATE;
251  }
252  }
253 
254  //Failed to establish connection with the Modbus/TCP server?
255  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
256  {
257  //Clean up side effects
259  //Update Modbus/TCP client state
260  context->state = MODBUS_CLIENT_STATE_DISCONNECTED;
261  }
262 
263  //Return status code
264  return error;
265 }
266 
267 
268 /**
269  * @brief Read coils
270  *
271  * This function code is used to read from 1 to 2000 contiguous status of
272  * coils in a remote device. The request specifies the starting address and
273  * the number of coils
274  *
275  * @param[in] context Pointer to the Modbus/TCP client context
276  * @param[in] address Address of the first coil
277  * @param[in] quantity Number of coils
278  * @param[out] value Value of the discrete outputs
279  * @return Error code
280  **/
281 
283  uint16_t address, uint_t quantity, uint8_t *value)
284 {
285  error_t error;
286 
287  //Check parameters
288  if(context == NULL || value == NULL)
290 
291  //The number of coils must be in range 1 to 2000
292  if(quantity < 1 || quantity > 2000)
294 
295  //Initialize status code
296  error = NO_ERROR;
297 
298  //Perform Modbus request/response transaction
299  while(!error)
300  {
301  //Check current state
302  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
303  {
304  //Format request
305  error = modbusClientFormatReadCoilsReq(context, address,
306  quantity);
307  }
308  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
309  context->state == MODBUS_CLIENT_STATE_RECEIVING)
310  {
311  //Send Modbus request and wait for a matching response
312  error = modbusClientTransaction(context);
313  }
314  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
315  {
316  //Parse response
317  error = modbusClientParseReadCoilsResp(context, quantity,
318  value);
319 
320  //The Modbus transaction is complete
321  context->state = MODBUS_CLIENT_STATE_CONNECTED;
322  break;
323  }
324  else
325  {
326  //Invalid state
327  error = ERROR_NOT_CONNECTED;
328  }
329  }
330 
331  //Return status code
332  return error;
333 }
334 
335 
336 /**
337  * @brief Read discrete inputs
338  *
339  * This function code is used to read from 1 to 2000 contiguous status of
340  * discrete inputs in a remote device. The request specifies the starting
341  * address and the number of inputs
342  *
343  * @param[in] context Pointer to the Modbus/TCP client context
344  * @param[in] address Address of the first input
345  * @param[in] quantity Number of inputs
346  * @param[out] value Value of the discrete inputs
347  * @return Error code
348  **/
349 
351  uint16_t address, uint_t quantity, uint8_t *value)
352 {
353  error_t error;
354 
355  //Check parameters
356  if(context == NULL || value == NULL)
358 
359  //The number of discrete inputs must be in range 1 to 2000
360  if(quantity < 1 || quantity > 2000)
362 
363  //Initialize status code
364  error = NO_ERROR;
365 
366  //Perform Modbus request/response transaction
367  while(!error)
368  {
369  //Check current state
370  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
371  {
372  //Format request
374  quantity);
375  }
376  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
377  context->state == MODBUS_CLIENT_STATE_RECEIVING)
378  {
379  //Send Modbus request and wait for a matching response
380  error = modbusClientTransaction(context);
381  }
382  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
383  {
384  //Parse response
385  error = modbusClientParseReadDiscreteInputsResp(context, quantity,
386  value);
387 
388  //The Modbus transaction is complete
389  context->state = MODBUS_CLIENT_STATE_CONNECTED;
390  break;
391  }
392  else
393  {
394  //Invalid state
395  error = ERROR_NOT_CONNECTED;
396  }
397  }
398 
399  //Return status code
400  return error;
401 }
402 
403 
404 /**
405  * @brief Read holding registers
406  *
407  * This function code is used to read the contents of a contiguous block of
408  * holding registers in a remote device. The request specifies the starting
409  * register address and the number of registers
410  *
411  * @param[in] context Pointer to the Modbus/TCP client context
412  * @param[in] address Starting register address
413  * @param[in] quantity Number of registers
414  * @param[out] value Value of the holding registers
415  * @return Error code
416  **/
417 
419  uint16_t address, uint_t quantity, uint16_t *value)
420 {
421  error_t error;
422 
423  //Check parameters
424  if(context == NULL || value == NULL)
426 
427  //The number of registers must be in range 1 to 125
428  if(quantity < 1 || quantity > 125)
430 
431  //Initialize status code
432  error = NO_ERROR;
433 
434  //Perform Modbus request/response transaction
435  while(!error)
436  {
437  //Check current state
438  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
439  {
440  //Format request
442  quantity);
443  }
444  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
445  context->state == MODBUS_CLIENT_STATE_RECEIVING)
446  {
447  //Send Modbus request and wait for a matching response
448  error = modbusClientTransaction(context);
449  }
450  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
451  {
452  //Parse response
453  error = modbusClientParseReadHoldingRegsResp(context, quantity,
454  value);
455 
456  //The Modbus transaction is complete
457  context->state = MODBUS_CLIENT_STATE_CONNECTED;
458  break;
459  }
460  else
461  {
462  //Invalid state
463  error = ERROR_NOT_CONNECTED;
464  }
465  }
466 
467  //Return status code
468  return error;
469 }
470 
471 
472 /**
473  * @brief Read input registers
474  *
475  * This function code is used to read from 1 to 125 contiguous input registers
476  * in a remote device. The request specifies the starting register address and
477  * the number of registers
478  *
479  * @param[in] context Pointer to the Modbus/TCP client context
480  * @param[in] address Starting register address
481  * @param[in] quantity Number of registers
482  * @param[out] value Value of the input registers
483  * @return Error code
484  **/
485 
487  uint16_t address, uint_t quantity, uint16_t *value)
488 {
489  error_t error;
490 
491  //Check parameters
492  if(context == NULL || value == NULL)
494 
495  //The number of registers must be in range 1 to 125
496  if(quantity < 1 || quantity > 125)
498 
499  //Initialize status code
500  error = NO_ERROR;
501 
502  //Perform Modbus request/response transaction
503  while(!error)
504  {
505  //Check current state
506  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
507  {
508  //Format request
510  quantity);
511  }
512  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
513  context->state == MODBUS_CLIENT_STATE_RECEIVING)
514  {
515  //Send Modbus request and wait for a matching response
516  error = modbusClientTransaction(context);
517  }
518  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
519  {
520  //Parse response
521  error = modbusClientParseReadInputRegsResp(context, quantity,
522  value);
523 
524  //The Modbus transaction is complete
525  context->state = MODBUS_CLIENT_STATE_CONNECTED;
526  break;
527  }
528  else
529  {
530  //Invalid state
531  error = ERROR_NOT_CONNECTED;
532  }
533  }
534 
535  //Return status code
536  return error;
537 }
538 
539 
540 /**
541  * @brief Write single coil
542  *
543  * This function code is used to write a single output to either ON or OFF in
544  * a remote device. The request specifies the address of the coil to be forced
545  * and the requested ON/OFF state
546  *
547  * @param[in] context Pointer to the Modbus/TCP client context
548  * @param[in] address Address of the coil to be forced
549  * @param[in] value Value of the discrete output
550  * @return Error code
551  **/
552 
554  uint16_t address, bool_t value)
555 {
556  error_t error;
557 
558  //Make sure the Modbus/TCP client context is valid
559  if(context == NULL)
561 
562  //Initialize status code
563  error = NO_ERROR;
564 
565  //Perform Modbus request/response transaction
566  while(!error)
567  {
568  //Check current state
569  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
570  {
571  //Format request
573  }
574  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
575  context->state == MODBUS_CLIENT_STATE_RECEIVING)
576  {
577  //Send Modbus request and wait for a matching response
578  error = modbusClientTransaction(context);
579  }
580  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
581  {
582  //Parse response
584 
585  //The Modbus transaction is complete
586  context->state = MODBUS_CLIENT_STATE_CONNECTED;
587  break;
588  }
589  else
590  {
591  //Invalid state
592  error = ERROR_NOT_CONNECTED;
593  }
594  }
595 
596  //Return status code
597  return error;
598 }
599 
600 
601 /**
602  * @brief Write single register
603  *
604  * This function code is used to write a single holding register in a remote
605  * device. The request specifies the address of the register to be written and
606  * the register value
607  *
608  * @param[in] context Pointer to the Modbus/TCP client context
609  * @param[in] address Address of the register to be written
610  * @param[in] value Register value
611  * @return Error code
612  **/
613 
615  uint16_t address, uint16_t value)
616 {
617  error_t error;
618 
619  //Make sure the Modbus/TCP client context is valid
620  if(context == NULL)
622 
623  //Initialize status code
624  error = NO_ERROR;
625 
626  //Perform Modbus request/response transaction
627  while(!error)
628  {
629  //Check current state
630  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
631  {
632  //Format request
634  }
635  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
636  context->state == MODBUS_CLIENT_STATE_RECEIVING)
637  {
638  //Send Modbus request and wait for a matching response
639  error = modbusClientTransaction(context);
640  }
641  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
642  {
643  //Parse response
645 
646  //The Modbus transaction is complete
647  context->state = MODBUS_CLIENT_STATE_CONNECTED;
648  break;
649  }
650  else
651  {
652  //Invalid state
653  error = ERROR_NOT_CONNECTED;
654  }
655  }
656 
657  //Return status code
658  return error;
659 }
660 
661 
662 /**
663  * @brief Write multiple coils
664  *
665  * This function code is used to force each coil in a sequence of coils to
666  * either ON or OFF in a remote device. The request specifies the starting
667  * address, the number of outputs and the requested ON/OFF states
668  *
669  * @param[in] context Pointer to the Modbus/TCP client context
670  * @param[in] address Address of the first coil to be forced
671  * @param[in] quantity Number of coils
672  * @param[in] value Value of the discrete outputs
673  * @return Error code
674  **/
675 
677  uint16_t address, uint_t quantity, const uint8_t *value)
678 {
679  error_t error;
680 
681  //Check parameters
682  if(context == NULL || value == NULL)
684 
685  //The number of coils must be in range 1 to 1968
686  if(quantity < 1 || quantity > 1968)
688 
689  //Initialize status code
690  error = NO_ERROR;
691 
692  //Perform Modbus request/response transaction
693  while(!error)
694  {
695  //Check current state
696  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
697  {
698  //Format request
700  quantity, value);
701  }
702  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
703  context->state == MODBUS_CLIENT_STATE_RECEIVING)
704  {
705  //Send Modbus request and wait for a matching response
706  error = modbusClientTransaction(context);
707  }
708  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
709  {
710  //Parse response
712  quantity);
713 
714  //The Modbus transaction is complete
715  context->state = MODBUS_CLIENT_STATE_CONNECTED;
716  break;
717  }
718  else
719  {
720  //Invalid state
721  error = ERROR_NOT_CONNECTED;
722  }
723  }
724 
725  //Return status code
726  return error;
727 }
728 
729 
730 /**
731  * @brief Write multiple registers
732  *
733  * This function code is used to write a block of contiguous registers (1 to
734  * 123 registers) in a remote device. The request specifies the starting
735  * address, the number of registers and the requested written values
736  *
737  * @param[in] context Pointer to the Modbus/TCP client context
738  * @param[in] address Starting register address
739  * @param[in] quantity Number of registers
740  * @param[in] value Value of the holding registers
741  * @return Error code
742  **/
743 
745  uint16_t address, uint_t quantity, const uint16_t *value)
746 {
747  error_t error;
748 
749  //Check parameters
750  if(context == NULL || value == NULL)
752 
753  //The number of registers must be in range 1 to 123
754  if(quantity < 1 || quantity > 123)
756 
757  //Initialize status code
758  error = NO_ERROR;
759 
760  //Perform Modbus request/response transaction
761  while(!error)
762  {
763  //Check current state
764  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
765  {
766  //Format request
768  quantity, value);
769  }
770  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
771  context->state == MODBUS_CLIENT_STATE_RECEIVING)
772  {
773  //Send Modbus request and wait for a matching response
774  error = modbusClientTransaction(context);
775  }
776  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
777  {
778  //Parse response
780  quantity);
781 
782  //The Modbus transaction is complete
783  context->state = MODBUS_CLIENT_STATE_CONNECTED;
784  break;
785  }
786  else
787  {
788  //Invalid state
789  error = ERROR_NOT_CONNECTED;
790  }
791  }
792 
793  //Return status code
794  return error;
795 }
796 
797 
798 /**
799  * @brief Apply AND/OR bitmask to a register
800  *
801  * This function code is used to modify the contents of a specified holding
802  * register using a combination of an AND mask, an OR mask, and the register's
803  * current contents. The function can be used to set or clear individual bits
804  * in the register
805  *
806  * @param[in] context Pointer to the Modbus/TCP client context
807  * @param[in] address Address of the holding register
808  * @param[in] andMask AND bitmask
809  * @param[in] orMask OR bitmask
810  * @return Error code
811  **/
812 
814  uint16_t address, uint16_t andMask, uint16_t orMask)
815 {
816  error_t error;
817 
818  //Make sure the Modbus/TCP client context is valid
819  if(context == NULL)
821 
822  //Initialize status code
823  error = NO_ERROR;
824 
825  //Perform Modbus request/response transaction
826  while(!error)
827  {
828  //Check current state
829  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
830  {
831  //Format request
833  andMask, orMask);
834  }
835  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
836  context->state == MODBUS_CLIENT_STATE_RECEIVING)
837  {
838  //Send Modbus request and wait for a matching response
839  error = modbusClientTransaction(context);
840  }
841  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
842  {
843  //Parse response
845  andMask, orMask);
846 
847  //The Modbus transaction is complete
848  context->state = MODBUS_CLIENT_STATE_CONNECTED;
849  break;
850  }
851  else
852  {
853  //Invalid state
854  error = ERROR_NOT_CONNECTED;
855  }
856  }
857 
858  //Return status code
859  return error;
860 }
861 
862 
863 /**
864  * @brief Read/write multiple registers
865  *
866  * This function code performs a combination of one read operation and one
867  * write operation in a single Modbus transaction. The write operation is
868  * performed before the read
869  *
870  * @param[in] context Pointer to the Modbus/TCP client context
871  * @param[in] readAddress Address of the first holding registers to be read
872  * @param[in] readQuantity Number of holding registers to be read
873  * @param[out] readValue Value of the holding registers (read operation)
874  * @param[in] writeAddress Address of the first holding registers to be written
875  * @param[in] writeQuantity Number of holding registers to be written
876  * @param[in] writeValue Value of the holding registers (write operation)
877  * @return Error code
878  **/
879 
881  uint16_t readAddress, uint_t readQuantity, uint16_t *readValue,
882  uint16_t writeAddress, uint_t writeQuantity, const uint16_t *writeValue)
883 {
884  error_t error;
885 
886  //Check parameters
887  if(context == NULL || readValue == NULL || writeValue == NULL)
889 
890  //The number of registers to be read must be in range 1 to 125
891  if(readQuantity < 1 || readQuantity > 125)
893 
894  //The number of registers to be written must be in range 1 to 121
895  if(writeQuantity < 1 || writeQuantity > 121)
897 
898  //Initialize status code
899  error = NO_ERROR;
900 
901  //Perform Modbus request/response transaction
902  while(!error)
903  {
904  //Check current state
905  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
906  {
907  //Format request
909  readAddress, readQuantity, writeAddress, writeQuantity, writeValue);
910  }
911  else if(context->state == MODBUS_CLIENT_STATE_SENDING ||
912  context->state == MODBUS_CLIENT_STATE_RECEIVING)
913  {
914  //Send Modbus request and wait for a matching response
915  error = modbusClientTransaction(context);
916  }
917  else if(context->state == MODBUS_CLIENT_STATE_COMPLETE)
918  {
919  //Parse response
921  readQuantity, readValue);
922 
923  //The Modbus transaction is complete
924  context->state = MODBUS_CLIENT_STATE_CONNECTED;
925  break;
926  }
927  else
928  {
929  //Invalid state
930  error = ERROR_NOT_CONNECTED;
931  }
932  }
933 
934  //Return status code
935  return error;
936 }
937 
938 
939 /**
940  * @brief Retrieve exception code
941  * @param[in] context Pointer to the Modbus/TCP client context
942  * @param[out] exceptionCode Exception code
943  * @return Error code
944  **/
945 
948 {
949  //Check parameters
950  if(context == NULL || exceptionCode == NULL)
952 
953  //Retrieve exception code
954  *exceptionCode = context->exceptionCode;
955 
956  //Successful processing
957  return NO_ERROR;
958 }
959 
960 
961 /**
962  * @brief Gracefully disconnect from the Modbus/TCP server
963  * @param[in] context Pointer to the Modbus/TCP client context
964  * @return Error code
965  **/
966 
968 {
969  error_t error;
970 
971  //Make sure the Modbus/TCP client context is valid
972  if(context == NULL)
974 
975  //Initialize status code
976  error = NO_ERROR;
977 
978  //Gracefully disconnect from the Modbus/TCP server
979  while(!error)
980  {
981  //Check current state
982  if(context->state == MODBUS_CLIENT_STATE_CONNECTED)
983  {
984  //Save current time
985  context->timestamp = osGetSystemTime();
986  //Update Modbus/TCP client state
987  context->state = MODBUS_CLIENT_STATE_DISCONNECTING;
988  }
989  else if(context->state == MODBUS_CLIENT_STATE_DISCONNECTING)
990  {
991  //Shutdown connection
992  error = modbusClientShutdownConnection(context);
993 
994  //Check status code
995  if(error == NO_ERROR)
996  {
997  //Close connection
999  //Update Modbus/TCP client state
1000  context->state = MODBUS_CLIENT_STATE_DISCONNECTED;
1001  }
1002  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1003  {
1004  //Check whether the timeout has elapsed
1005  error = modbusClientCheckTimeout(context);
1006  }
1007  else
1008  {
1009  //A communication error has occurred
1010  }
1011  }
1012  else if(context->state == MODBUS_CLIENT_STATE_DISCONNECTED)
1013  {
1014  //The Modbus/TCP client is disconnected
1015  break;
1016  }
1017  else
1018  {
1019  //Invalid state
1020  error = ERROR_WRONG_STATE;
1021  }
1022  }
1023 
1024  //Failed to gracefully disconnect from the Modbus/TCP server?
1025  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
1026  {
1027  //Close connection
1028  modbusClientCloseConnection(context);
1029  //Update Modbus/TCP client state
1030  context->state = MODBUS_CLIENT_STATE_DISCONNECTED;
1031  }
1032 
1033  //Return status code
1034  return error;
1035 }
1036 
1037 
1038 /**
1039  * @brief Close the connection with the Modbus/TCP server
1040  * @param[in] context Pointer to the Modbus/TCP client context
1041  * @return Error code
1042  **/
1043 
1045 {
1046  //Make sure the Modbus/TCP client context is valid
1047  if(context == NULL)
1048  return ERROR_INVALID_PARAMETER;
1049 
1050  //Close connection
1051  modbusClientCloseConnection(context);
1052  //Update Modbus/TCP client state
1053  context->state = MODBUS_CLIENT_STATE_DISCONNECTED;
1054 
1055  //Successful processing
1056  return NO_ERROR;
1057 }
1058 
1059 
1060 /**
1061  * @brief Release Modbus/TCP client context
1062  * @param[in] context Pointer to the Modbus/TCP client context
1063  **/
1064 
1066 {
1067  //Make sure the Modbus/TCP client context is valid
1068  if(context != NULL)
1069  {
1070  //Close connection
1071  modbusClientCloseConnection(context);
1072 
1073 #if (MODBUS_CLIENT_TLS_SUPPORT == ENABLED)
1074  //Release TLS session state
1075  tlsFreeSessionState(&context->tlsSession);
1076 #endif
1077 
1078  //Clear Modbus/TCP client context
1079  osMemset(context, 0, sizeof(ModbusClientContext));
1080  }
1081 }
1082 
1083 #endif
error_t modbusClientParseWriteMultipleRegsResp(ModbusClientContext *context, uint16_t address, uint_t quantity)
Parse Write Multiple Registers response.
error_t modbusClientWriteSingleReg(ModbusClientContext *context, uint16_t address, uint16_t value)
Write single register.
Modbus/TCP client.
#define MODBUS_CLIENT_DEFAULT_TIMEOUT
Definition: modbus_client.h:54
int bool_t
Definition: compiler_port.h:63
error_t modbusClientReadWriteMultipleRegs(ModbusClientContext *context, uint16_t readAddress, uint_t readQuantity, uint16_t *readValue, uint16_t writeAddress, uint_t writeQuantity, const uint16_t *writeValue)
Read/write multiple registers.
@ ERROR_WOULD_BLOCK
Definition: error.h:96
IP network address.
Definition: ip.h:90
error_t modbusClientConnect(ModbusClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish connection with the Modbus/TCP server.
error_t modbusClientParseReadHoldingRegsResp(ModbusClientContext *context, uint_t quantity, uint16_t *value)
Parse Read Holding Registers response.
error_t modbusClientParseWriteMultipleCoilsResp(ModbusClientContext *context, uint16_t address, uint_t quantity)
Parse Write Multiple Coils response.
uint16_t andMask
error_t modbusClientRegisterTlsInitCallback(ModbusClientContext *context, ModbusClientTlsInitCallback callback)
Register TLS initialization callback function.
error_t modbusClientOpenConnection(ModbusClientContext *context)
Open network connection.
error_t modbusClientReadInputRegs(ModbusClientContext *context, uint16_t address, uint_t quantity, uint16_t *value)
Read input registers.
error_t modbusClientFormatReadWriteMultipleRegsReq(ModbusClientContext *context, uint16_t readAddress, uint16_t readQuantity, uint16_t writeAddress, uint16_t writeQuantity, const uint16_t *writeValue)
Format Read/Write Multiple Registers request.
error_t modbusClientParseWriteSingleCoilResp(ModbusClientContext *context, uint16_t address, bool_t value)
Parse Write Single Coil response.
error_t modbusClientFormatWriteMultipleRegsReq(ModbusClientContext *context, uint16_t address, uint_t quantity, const uint16_t *value)
Format Write Multiple Registers request.
error_t modbusClientFormatMaskWriteRegReq(ModbusClientContext *context, uint16_t address, uint16_t andMask, uint16_t orMask)
Format Mask Write Register request.
#define ModbusClientContext
Definition: modbus_client.h:86
error_t modbusClientMaskWriteReg(ModbusClientContext *context, uint16_t address, uint16_t andMask, uint16_t orMask)
Apply AND/OR bitmask to a register.
error_t modbusClientEstablishConnection(ModbusClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish network connection.
void tlsFreeSessionState(TlsSessionState *session)
Properly dispose a session state.
Definition: tls.c:3065
error_t modbusClientInit(ModbusClientContext *context)
Initialize Modbus/TCP client context.
Definition: modbus_client.c:51
@ ERROR_WRONG_STATE
Definition: error.h:210
error_t modbusClientFormatReadCoilsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Coils request.
error_t modbusClientFormatReadInputRegsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Input Registers request.
@ MODBUS_CLIENT_STATE_CONNECTING
error_t modbusClientFormatWriteSingleCoilReq(ModbusClientContext *context, uint16_t address, bool_t value)
Format Write Single Coil request.
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
uint32_t netGetRand(NetContext *context)
Generate a random 32-bit value.
Definition: net.c:441
void modbusClientCloseConnection(ModbusClientContext *context)
Close network connection.
error_t modbusClientGetExceptionCode(ModbusClientContext *context, ModbusExceptionCode *exceptionCode)
Retrieve exception code.
error_t
Error codes.
Definition: error.h:43
error_t modbusClientParseReadInputRegsResp(ModbusClientContext *context, uint_t quantity, uint16_t *value)
Parse Read Input Registers response.
error_t modbusClientBindToInterface(ModbusClientContext *context, NetInterface *interface)
Bind the Modbus/TCP client to a particular network interface.
error_t modbusClientParseReadDiscreteInputsResp(ModbusClientContext *context, uint_t quantity, uint8_t *value)
Parse Discrete Inputs response.
@ MODBUS_CLIENT_STATE_SENDING
#define NetInterface
Definition: net.h:40
NetContext * netGetDefaultContext(void)
Get default TCP/IP stack context.
Definition: net.c:527
@ MODBUS_CLIENT_STATE_DISCONNECTING
error_t modbusClientSetTimeout(ModbusClientContext *context, systime_t timeout)
Set timeout value for blocking operations.
@ MODBUS_CLIENT_STATE_DISCONNECTED
error_t modbusClientCheckTimeout(ModbusClientContext *context)
Determine whether a timeout error has occurred.
@ MODBUS_CLIENT_STATE_RECEIVING
error_t modbusClientWriteMultipleRegs(ModbusClientContext *context, uint16_t address, uint_t quantity, const uint16_t *value)
Write multiple registers.
#define MODBUS_DEFAULT_UNIT_ID
Definition: modbus_common.h:45
Modbus PDU formatting and parsing.
uint32_t systime_t
System time.
void modbusClientDeinit(ModbusClientContext *context)
Release Modbus/TCP client context.
Transport protocol abstraction layer.
@ ERROR_TIMEOUT
Definition: error.h:95
error_t modbusClientClose(ModbusClientContext *context)
Close the connection with the Modbus/TCP server.
uint8_t unitId
error_t(* ModbusClientTlsInitCallback)(ModbusClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
uint16_t orMask
@ ERROR_NOT_CONNECTED
Definition: error.h:80
error_t modbusClientWriteMultipleCoils(ModbusClientContext *context, uint16_t address, uint_t quantity, const uint8_t *value)
Write multiple coils.
Ipv6Addr address[]
Definition: ipv6.h:345
error_t modbusClientReadCoils(ModbusClientContext *context, uint16_t address, uint_t quantity, uint8_t *value)
Read coils.
error_t modbusClientFormatWriteSingleRegReq(ModbusClientContext *context, uint16_t address, uint16_t value)
Format Write Single Register request.
uint8_t value[]
Definition: tcp.h:376
error_t modbusClientParseReadCoilsResp(ModbusClientContext *context, uint_t quantity, uint8_t *value)
Parse Read Coils response.
error_t modbusClientFormatWriteMultipleCoilsReq(ModbusClientContext *context, uint16_t address, uint_t quantity, const uint8_t *value)
Format Write Multiple Coils request.
error_t modbusClientSetUnitId(ModbusClientContext *context, uint8_t unitId)
Set unit identifier.
error_t modbusClientFormatReadDiscreteInputsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Discrete Inputs request.
error_t modbusClientParseReadWriteMultipleRegsResp(ModbusClientContext *context, uint_t readQuantity, uint16_t *readValue)
Parse Read/Write Multiple Registers response.
error_t modbusClientParseWriteSingleRegResp(ModbusClientContext *context, uint16_t address, uint16_t value)
Parse Write Single Register response.
error_t modbusClientWriteSingleCoil(ModbusClientContext *context, uint16_t address, bool_t value)
Write single coil.
error_t modbusClientShutdownConnection(ModbusClientContext *context)
Shutdown network connection.
error_t modbusClientDisconnect(ModbusClientContext *context)
Gracefully disconnect from the Modbus/TCP server.
unsigned int uint_t
Definition: compiler_port.h:57
error_t modbusClientParseMaskWriteRegResp(ModbusClientContext *context, uint16_t address, uint16_t andMask, uint16_t orMask)
Parse Mask Write Register response.
#define osMemset(p, value, length)
Definition: os_port.h:138
@ MODBUS_CLIENT_STATE_CONNECTED
ModbusExceptionCode
Modbus exception codes.
error_t modbusClientReadHoldingRegs(ModbusClientContext *context, uint16_t address, uint_t quantity, uint16_t *value)
Read holding registers.
error_t tlsInitSessionState(TlsSessionState *session)
Initialize session state.
Definition: tls.c:2922
uint8_t exceptionCode
error_t modbusClientFormatReadHoldingRegsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Holding Registers request.
Helper functions for Modbus/TCP client.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
@ MODBUS_CLIENT_STATE_COMPLETE
error_t modbusClientTransaction(ModbusClientContext *context)
Perform Modbus transaction.
systime_t osGetSystemTime(void)
Retrieve system time.
error_t modbusClientReadDiscreteInputs(ModbusClientContext *context, uint16_t address, uint_t quantity, uint8_t *value)
Read discrete inputs.