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