modbus_client_pdu.c
Go to the documentation of this file.
1 /**
2  * @file modbus_client_pdu.c
3  * @brief Modbus PDU formatting and parsing
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 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.2.4
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"
38 #include "modbus/modbus_debug.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (MODBUS_CLIENT_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Format Read Coils request
47  * @param[in] context Pointer to the Modbus/TCP client context
48  * @param[in] address Address of the first coil
49  * @param[in] quantity Number of coils
50  * @return Error code
51  **/
52 
54  uint16_t address, uint_t quantity)
55 {
56  size_t length;
57  ModbusReadCoilsReq *request;
58 
59  //Point to the Modbus request PDU
60  request = modbusClientGetRequestPdu(context);
61 
62  //Format Read Coils request
63  request->functionCode = MODBUS_FUNCTION_READ_COILS;
64  request->startingAddr = htons(address);
65  request->quantityOfCoils = htons(quantity);
66 
67  //Compute the length of the request PDU
68  length = sizeof(ModbusReadCoilsReq);
69 
70  //Debug message
71  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
72  //Dump the contents of the PDU for debugging purpose
73  modbusDumpRequestPdu(request, length);
74 
75  //Format MBAP header
76  return modbusClientFormatMbapHeader(context, length);
77 }
78 
79 
80 /**
81  * @brief Format Read Discrete Inputs request
82  * @param[in] context Pointer to the Modbus/TCP client context
83  * @param[in] address Address of the first coil
84  * @param[in] quantity Number of inputs
85  * @return Error code
86  **/
87 
89  uint16_t address, uint_t quantity)
90 {
91  size_t length;
93 
94  //Point to the Modbus request PDU
95  request = modbusClientGetRequestPdu(context);
96 
97  //Format Read Discrete Inputs request
98  request->functionCode = MODBUS_FUNCTION_READ_DISCRETE_INPUTS;
99  request->startingAddr = htons(address);
100  request->quantityOfInputs = htons(quantity);
101 
102  //Compute the length of the request PDU
104 
105  //Debug message
106  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
107  //Dump the contents of the PDU for debugging purpose
108  modbusDumpRequestPdu(request, length);
109 
110  //Format MBAP header
111  return modbusClientFormatMbapHeader(context, length);
112 }
113 
114 
115 /**
116  * @brief Format Read Holding Registers request
117  * @param[in] context Pointer to the Modbus/TCP client context
118  * @param[in] address Starting register address
119  * @param[in] quantity Number of registers
120  * @return Error code
121  **/
122 
124  uint16_t address, uint_t quantity)
125 {
126  size_t length;
127  ModbusReadHoldingRegsReq *request;
128 
129  //Point to the Modbus request PDU
130  request = modbusClientGetRequestPdu(context);
131 
132  //Format Read Holding Registers request
133  request->functionCode = MODBUS_FUNCTION_READ_HOLDING_REGS;
134  request->startingAddr = htons(address);
135  request->quantityOfRegs = htons(quantity);
136 
137  //Compute the length of the request PDU
139 
140  //Debug message
141  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
142  //Dump the contents of the PDU for debugging purpose
143  modbusDumpRequestPdu(request, length);
144 
145  //Format MBAP header
146  return modbusClientFormatMbapHeader(context, length);
147 }
148 
149 
150 /**
151  * @brief Format Read Input Registers request
152  * @param[in] context Pointer to the Modbus/TCP client context
153  * @param[in] address Starting register address
154  * @param[in] quantity Number of registers
155  * @return Error code
156  **/
157 
159  uint16_t address, uint_t quantity)
160 {
161  size_t length;
162  ModbusReadInputRegsReq *request;
163 
164  //Point to the Modbus request PDU
165  request = modbusClientGetRequestPdu(context);
166 
167  //Format Read Input Registers request
168  request->functionCode = MODBUS_FUNCTION_READ_INPUT_REGS;
169  request->startingAddr = htons(address);
170  request->quantityOfRegs = htons(quantity);
171 
172  //Compute the length of the request PDU
173  length = sizeof(ModbusReadInputRegsReq);
174 
175  //Debug message
176  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
177  //Dump the contents of the PDU for debugging purpose
178  modbusDumpRequestPdu(request, length);
179 
180  //Format MBAP header
181  return modbusClientFormatMbapHeader(context, length);
182 }
183 
184 
185 /**
186  * @brief Format Write Single Coil request
187  * @param[in] context Pointer to the Modbus/TCP client context
188  * @param[in] address Address of the coil to be forced
189  * @param[in] value Value of the discrete output
190  * @return Error code
191  **/
192 
194  uint16_t address, bool_t value)
195 {
196  size_t length;
197  ModbusWriteSingleCoilReq *request;
198 
199  //Point to the Modbus request PDU
200  request = modbusClientGetRequestPdu(context);
201 
202  //Format Write Single Coil request
203  request->functionCode = MODBUS_FUNCTION_WRITE_SINGLE_COIL;
204  request->outputAddr = htons(address);
205 
206  //A value of 0xFF00 requests the output to be ON. A value of 0x0000
207  //requests it to be OFF
208  if(value)
209  request->outputValue = HTONS(MODBUS_COIL_STATE_ON);
210  else
211  request->outputValue = HTONS(MODBUS_COIL_STATE_OFF);
212 
213  //Compute the length of the request PDU
215 
216  //Debug message
217  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
218  //Dump the contents of the PDU for debugging purpose
219  modbusDumpRequestPdu(request, length);
220 
221  //Format MBAP header
222  return modbusClientFormatMbapHeader(context, length);
223 }
224 
225 
226 /**
227  * @brief Format Write Single Register request
228  * @param[in] context Pointer to the Modbus/TCP client context
229  * @param[in] address Address of the register to be written
230  * @param[in] value Register value
231  * @return Error code
232  **/
233 
235  uint16_t address, uint16_t value)
236 {
237  size_t length;
238  ModbusWriteSingleRegReq *request;
239 
240  //Point to the Modbus request PDU
241  request = modbusClientGetRequestPdu(context);
242 
243  //Format Write Single Register request
244  request->functionCode = MODBUS_FUNCTION_WRITE_SINGLE_REG;
245  request->regAddr = htons(address);
246  request->regValue = htons(value);
247 
248  //Compute the length of the request PDU
250 
251  //Debug message
252  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
253  //Dump the contents of the PDU for debugging purpose
254  modbusDumpRequestPdu(request, length);
255 
256  //Format MBAP header
257  return modbusClientFormatMbapHeader(context, length);
258 }
259 
260 
261 /**
262  * @brief Format Write Multiple Coils request
263  * @param[in] context Pointer to the Modbus/TCP client context
264  * @param[in] address Address of the first coil to be forced
265  * @param[in] quantity Number of coils
266  * @param[in] value Value of the discrete outputs
267  * @return Error code
268  **/
269 
271  uint16_t address, uint_t quantity, const uint8_t *value)
272 {
273  uint8_t mask;
274  size_t length;
276 
277  //Point to the Modbus request PDU
278  request = modbusClientGetRequestPdu(context);
279 
280  //Format Write Multiple Coils request
281  request->functionCode = MODBUS_FUNCTION_WRITE_MULTIPLE_COILS;
282  request->startingAddr = htons(address);
283  request->quantityOfOutputs = htons(quantity);
284  request->byteCount = (quantity + 7) / 8;
285 
286  //Copy coil values
287  osMemcpy(request->outputValue, value, request->byteCount);
288 
289  //Unused bits in the last data byte should be zero–filled
290  if((quantity % 8) != 0)
291  {
292  mask = (1 << (quantity % 8)) - 1;
293  request->outputValue[request->byteCount - 1] &= mask;
294  }
295 
296  //Compute the length of the request PDU
297  length = sizeof(ModbusWriteMultipleCoilsReq) + request->byteCount;
298 
299  //Debug message
300  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
301  //Dump the contents of the PDU for debugging purpose
302  modbusDumpRequestPdu(request, length);
303 
304  //Format MBAP header
305  return modbusClientFormatMbapHeader(context, length);
306 }
307 
308 
309 /**
310  * @brief Format Write Multiple Registers request
311  * @param[in] context Pointer to the Modbus/TCP client context
312  * @param[in] address Starting register address
313  * @param[in] quantity Number of registers
314  * @param[in] value Value of the holding registers
315  * @return Error code
316  **/
317 
319  uint16_t address, uint_t quantity, const uint16_t *value)
320 {
321  uint_t i;
322  size_t length;
324 
325  //Point to the Modbus request PDU
326  request = modbusClientGetRequestPdu(context);
327 
328  //Format Write Multiple Registers request
329  request->functionCode = MODBUS_FUNCTION_WRITE_MULTIPLE_REGS;
330  request->startingAddr = htons(address);
331  request->quantityOfRegs = htons(quantity);
332  request->byteCount = quantity * sizeof(uint16_t);
333 
334  //Copy register values
335  for(i = 0; i < quantity; i++)
336  {
337  request->regValue[i] = ntohs(value[i]);
338  }
339 
340  //Compute the length of the request PDU
341  length = sizeof(ModbusWriteMultipleRegsReq) + request->byteCount;
342 
343  //Debug message
344  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
345  //Dump the contents of the PDU for debugging purpose
346  modbusDumpRequestPdu(request, length);
347 
348  //Format MBAP header
349  return modbusClientFormatMbapHeader(context, length);
350 }
351 
352 
353 /**
354  * @brief Format Mask Write Register request
355  * @param[in] context Pointer to the Modbus/TCP client context
356  * @param[in] address Address of the holding register
357  * @param[in] andMask AND bitmask
358  * @param[in] orMask OR bitmask
359  * @return Error code
360  **/
361 
363  uint16_t address, uint16_t andMask, uint16_t orMask)
364 {
365  size_t length;
366  ModbusMaskWriteRegReq *request;
367 
368  //Point to the Modbus request PDU
369  request = modbusClientGetRequestPdu(context);
370 
371  //Format Write Single Register request
372  request->functionCode = MODBUS_FUNCTION_MASK_WRITE_REG;
373  request->referenceAddr = htons(address);
374  request->andMask = htons(andMask);
375  request->orMask = htons(orMask);
376 
377  //Compute the length of the request PDU
378  length = sizeof(ModbusMaskWriteRegReq);
379 
380  //Debug message
381  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
382  //Dump the contents of the PDU for debugging purpose
383  modbusDumpRequestPdu(request, length);
384 
385  //Format MBAP header
386  return modbusClientFormatMbapHeader(context, length);
387 }
388 
389 
390 /**
391  * @brief Format Read/Write Multiple Registers request
392  * @param[in] context Pointer to the Modbus/TCP client context
393  * @param[in] readAddress Address of the first holding registers to be read
394  * @param[in] readQuantity Number of holding registers to be read
395  * @param[in] writeAddress Address of the first holding registers to be written
396  * @param[in] writeQuantity Number of holding registers to be written
397  * @param[in] writeValue Value of the holding registers (write operation)
398  * @return Error code
399  **/
400 
402  uint16_t readAddress, uint16_t readQuantity, uint16_t writeAddress,
403  uint16_t writeQuantity, const uint16_t *writeValue)
404 {
405  uint_t i;
406  size_t length;
408 
409  //Point to the Modbus request PDU
410  request = modbusClientGetRequestPdu(context);
411 
412  //Format Read/Write Multiple Registers request
413  request->functionCode = MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS;
414  request->readStartingAddr = htons(readAddress);
415  request->quantityToRead = htons(readQuantity);
416  request->writeStartingAddr = htons(writeAddress);
417  request->quantityToWrite = htons(writeQuantity);
418  request->writeByteCount = writeQuantity * sizeof(uint16_t);
419 
420  //Copy register values
421  for(i = 0; i < writeQuantity; i++)
422  {
423  request->writeRegValue[i] = ntohs(writeValue[i]);
424  }
425 
426  //Compute the length of the request PDU
427  length = sizeof(ModbusReadWriteMultipleRegsReq) + request->writeByteCount;
428 
429  //Debug message
430  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
431  //Dump the contents of the PDU for debugging purpose
432  modbusDumpRequestPdu(request, length);
433 
434  //Format MBAP header
435  return modbusClientFormatMbapHeader(context, length);
436 }
437 
438 
439 /**
440  * @brief Parse Read Coils response
441  * @param[in] context Pointer to the Modbus/TCP client context
442  * @param[in] quantity Number of coils
443  * @param[out] value Value of the discrete outputs
444  * @return Error code
445  **/
446 
448  uint_t quantity, uint8_t *value)
449 {
450  size_t n;
451  size_t length;
452  ModbusReadCoilsResp *response;
453 
454  //Point to the Modbus response PDU
455  response = modbusClientGetResponsePdu(context, &length);
456 
457  //Malformed PDU?
458  if(length < sizeof(ModbusReadCoilsResp))
459  return ERROR_INVALID_LENGTH;
460 
461  //Compute the length of the data field
462  n = length - sizeof(ModbusReadCoilsResp);
463 
464  //Check function code
465  if(response->functionCode != MODBUS_FUNCTION_READ_COILS)
466  return ERROR_INVALID_RESPONSE;
467 
468  //Check byte count field
469  if(response->byteCount != n || response->byteCount != ((quantity + 7) / 8))
470  return ERROR_INVALID_LENGTH;
471 
472  //Copy coil values
473  osMemcpy(value, response->coilStatus, response->byteCount);
474 
475  //Successful processing
476  return NO_ERROR;
477 }
478 
479 
480 /**
481  * @brief Parse Discrete Inputs response
482  * @param[in] context Pointer to the Modbus/TCP client context
483  * @param[in] quantity Number of inputs
484  * @param[out] value Value of the discrete inputs
485  * @return Error code
486  **/
487 
489  uint_t quantity, uint8_t *value)
490 {
491  size_t n;
492  size_t length;
494 
495  //Point to the Modbus response PDU
496  response = modbusClientGetResponsePdu(context, &length);
497 
498  //Malformed PDU?
500  return ERROR_INVALID_LENGTH;
501 
502  //Compute the length of the data field
504 
505  //Check function code
506  if(response->functionCode != MODBUS_FUNCTION_READ_DISCRETE_INPUTS)
507  return ERROR_INVALID_RESPONSE;
508 
509  //Check byte count field
510  if(response->byteCount != n || response->byteCount != ((quantity + 7) / 8))
511  return ERROR_INVALID_LENGTH;
512 
513  //Copy discrete input values
514  osMemcpy(value, response->inputStatus, response->byteCount);
515 
516  //Successful processing
517  return NO_ERROR;
518 }
519 
520 
521 /**
522  * @brief Parse Read Holding Registers response
523  * @param[in] context Pointer to the Modbus/TCP client context
524  * @param[in] quantity Number of registers
525  * @param[out] value Value of the holding registers
526  * @return Error code
527  **/
528 
530  uint_t quantity, uint16_t *value)
531 {
532  uint_t i;
533  size_t n;
534  size_t length;
535  ModbusReadHoldingRegsResp *response;
536 
537  //Point to the Modbus response PDU
538  response = modbusClientGetResponsePdu(context, &length);
539 
540  //Malformed PDU?
541  if(length < sizeof(ModbusReadHoldingRegsResp))
542  return ERROR_INVALID_LENGTH;
543 
544  //Compute the length of the data field
545  n = length - sizeof(ModbusReadHoldingRegsResp);
546 
547  //Check function code
548  if(response->functionCode != MODBUS_FUNCTION_READ_HOLDING_REGS)
549  return ERROR_INVALID_RESPONSE;
550 
551  //Check byte count field
552  if(response->byteCount != n ||
553  response->byteCount != (quantity * sizeof(uint16_t)))
554  {
555  return ERROR_INVALID_LENGTH;
556  }
557 
558  //Copy register values
559  for(i = 0; i < quantity; i++)
560  {
561  value[i] = ntohs(response->regValue[i]);
562  }
563 
564  //Successful processing
565  return NO_ERROR;
566 }
567 
568 
569 /**
570  * @brief Parse Read Input Registers response
571  * @param[in] context Pointer to the Modbus/TCP client context
572  * @param[in] quantity Number of registers
573  * @param[out] value Value of the input registers
574  * @return Error code
575  **/
576 
578  uint_t quantity, uint16_t *value)
579 {
580  uint_t i;
581  size_t n;
582  size_t length;
583  ModbusReadInputRegsResp *response;
584 
585  //Point to the Modbus response PDU
586  response = modbusClientGetResponsePdu(context, &length);
587 
588  //Malformed PDU?
589  if(length < sizeof(ModbusReadInputRegsResp))
590  return ERROR_INVALID_LENGTH;
591 
592  //Compute the length of the data field
593  n = length - sizeof(ModbusReadInputRegsResp);
594 
595  //Check function code
596  if(response->functionCode != MODBUS_FUNCTION_READ_INPUT_REGS)
597  return ERROR_INVALID_RESPONSE;
598 
599  //Check byte count field
600  if(response->byteCount != n ||
601  response->byteCount != (quantity * sizeof(uint16_t)))
602  {
603  return ERROR_INVALID_LENGTH;
604  }
605 
606  //Copy register values
607  for(i = 0; i < quantity; i++)
608  {
609  value[i] = ntohs(response->regValue[i]);
610  }
611 
612  //Successful processing
613  return NO_ERROR;
614 }
615 
616 
617 /**
618  * @brief Parse Write Single Coil response
619  * @param[in] context Pointer to the Modbus/TCP client context
620  * @param[in] address Address of the coil to be forced
621  * @param[in] value Value of the discrete output
622  * @return Error code
623  **/
624 
626  uint16_t address, bool_t value)
627 {
628  size_t length;
629  bool_t referenceValue;
630  ModbusWriteSingleCoilResp *response;
631 
632  //Point to the Modbus response PDU
633  response = modbusClientGetResponsePdu(context, &length);
634 
635  //Malformed PDU?
636  if(length < sizeof(ModbusWriteSingleCoilResp))
637  return ERROR_INVALID_LENGTH;
638 
639  //Check function code
640  if(response->functionCode != MODBUS_FUNCTION_WRITE_SINGLE_COIL)
641  return ERROR_INVALID_RESPONSE;
642 
643  //A value of 0xFF00 requests the output to be ON. A value of 0x0000
644  //requests it to be OFF
645  referenceValue = value ? MODBUS_COIL_STATE_ON : MODBUS_COIL_STATE_OFF;
646 
647  //The normal response is an echo of the request
648  if(ntohs(response->outputAddr) != address ||
649  ntohs(response->outputValue) != referenceValue)
650  {
651  return ERROR_INVALID_RESPONSE;
652  }
653 
654  //Successful processing
655  return NO_ERROR;
656 }
657 
658 
659 /**
660  * @brief Parse Write Single Register response
661  * @param[in] context Pointer to the Modbus/TCP client context
662  * @param[in] address Address of the register to be written
663  * @param[in] value Register value
664  * @return Error code
665  **/
666 
668  uint16_t address, uint16_t value)
669 {
670  size_t length;
671  ModbusWriteSingleRegResp *response;
672 
673  //Point to the Modbus response PDU
674  response = modbusClientGetResponsePdu(context, &length);
675 
676  //Malformed PDU?
677  if(length < sizeof(ModbusWriteSingleRegResp))
678  return ERROR_INVALID_LENGTH;
679 
680  //Check function code
681  if(response->functionCode != MODBUS_FUNCTION_WRITE_SINGLE_REG)
682  return ERROR_INVALID_RESPONSE;
683 
684  //The normal response is an echo of the request
685  if(ntohs(response->regAddr) != address ||
686  ntohs(response->regValue) != value)
687  {
688  return ERROR_INVALID_RESPONSE;
689  }
690 
691  //Successful processing
692  return NO_ERROR;
693 }
694 
695 
696 /**
697  * @brief Parse Write Multiple Coils response
698  * @param[in] context Pointer to the Modbus/TCP client context
699  * @param[in] address Address of the first coil to be forced
700  * @param[in] quantity Number of coils
701  * @return Error code
702  **/
703 
705  uint16_t address, uint_t quantity)
706 {
707  size_t length;
709 
710  //Point to the Modbus response PDU
711  response = modbusClientGetResponsePdu(context, &length);
712 
713  //Malformed PDU?
715  return ERROR_INVALID_LENGTH;
716 
717  //Check function code
718  if(response->functionCode != MODBUS_FUNCTION_WRITE_MULTIPLE_COILS)
719  return ERROR_INVALID_RESPONSE;
720 
721  //The normal response returns the starting address, and quantity of
722  //coils forced
723  if(ntohs(response->startingAddr) != address ||
724  ntohs(response->quantityOfOutputs) != quantity)
725  {
726  return ERROR_INVALID_RESPONSE;
727  }
728 
729  //Successful processing
730  return NO_ERROR;
731 }
732 
733 
734 /**
735  * @brief Parse Write Multiple Registers response
736  * @param[in] context Pointer to the Modbus/TCP client context
737  * @param[in] address Starting register address
738  * @param[in] quantity Number of registers
739  * @return Error code
740  **/
741 
743  uint16_t address, uint_t quantity)
744 {
745  size_t length;
746  ModbusWriteMultipleRegsResp *response;
747 
748  //Point to the Modbus response PDU
749  response = modbusClientGetResponsePdu(context, &length);
750 
751  //Malformed PDU?
752  if(length < sizeof(ModbusWriteMultipleRegsResp))
753  return ERROR_INVALID_LENGTH;
754 
755  //Check function code
756  if(response->functionCode != MODBUS_FUNCTION_WRITE_MULTIPLE_REGS)
757  return ERROR_INVALID_RESPONSE;
758 
759  //The normal response returns the starting address, and quantity of
760  //registers written
761  if(ntohs(response->startingAddr) != address ||
762  ntohs(response->quantityOfRegs) != quantity)
763  {
764  return ERROR_INVALID_RESPONSE;
765  }
766 
767  //Successful processing
768  return NO_ERROR;
769 }
770 
771 
772 /**
773  * @brief Parse Mask Write Register response
774  * @param[in] context Pointer to the Modbus/TCP client context
775  * @param[in] address Address of the holding register
776  * @param[in] andMask AND bitmask
777  * @param[in] orMask OR bitmask
778  * @return Error code
779  **/
780 
782  uint16_t address, uint16_t andMask, uint16_t orMask)
783 {
784  size_t length;
785  ModbusMaskWriteRegResp *response;
786 
787  //Point to the Modbus response PDU
788  response = modbusClientGetResponsePdu(context, &length);
789 
790  //Malformed PDU?
791  if(length < sizeof(ModbusMaskWriteRegResp))
792  return ERROR_INVALID_LENGTH;
793 
794  //Check function code
795  if(response->functionCode != MODBUS_FUNCTION_MASK_WRITE_REG)
796  return ERROR_INVALID_RESPONSE;
797 
798  //The normal response is an echo of the request
799  if(ntohs(response->referenceAddr) != address ||
800  ntohs(response->andMask) != andMask ||
801  ntohs(response->orMask) != orMask)
802  {
803  return ERROR_INVALID_RESPONSE;
804  }
805 
806  //Successful processing
807  return NO_ERROR;
808 }
809 
810 
811 /**
812  * @brief Parse Read/Write Multiple Registers response
813  * @param[in] context Pointer to the Modbus/TCP client context
814  * @param[in] readQuantity Number of holding registers to be read
815  * @param[out] readValue Value of the holding registers
816  * @return Error code
817  **/
818 
820  uint_t readQuantity, uint16_t *readValue)
821 {
822  uint_t i;
823  size_t n;
824  size_t length;
826 
827  //Point to the Modbus response PDU
828  response = modbusClientGetResponsePdu(context, &length);
829 
830  //Malformed PDU?
832  return ERROR_INVALID_LENGTH;
833 
834  //Compute the length of the data field
835  n = length - sizeof(ModbusReadInputRegsResp);
836 
837  //Check function code
838  if(response->functionCode != MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS)
839  return ERROR_INVALID_RESPONSE;
840 
841  //Check byte count field
842  if(response->readByteCount != n ||
843  response->readByteCount != (readQuantity * sizeof(uint16_t)))
844  {
845  return ERROR_INVALID_LENGTH;
846  }
847 
848  //Copy register values
849  for(i = 0; i < readQuantity; i++)
850  {
851  readValue[i] = ntohs(response->readRegValue[i]);
852  }
853 
854  //Successful processing
855  return NO_ERROR;
856 }
857 
858 
859 /**
860  * @brief Parse Exception response
861  * @param[in] context Pointer to the Modbus/TCP client context
862  * @return Error code
863  **/
864 
866 {
867  size_t length;
868  ModbusExceptionResp *response;
869 
870  //Point to the Modbus response PDU
871  response = modbusClientGetResponsePdu(context, &length);
872 
873  //Malformed PDU?
874  if(length < sizeof(ModbusExceptionResp))
875  return ERROR_INVALID_LENGTH;
876 
877  //Save exception code
878  context->exceptionCode = (ModbusExceptionCode) response->exceptionCode;
879 
880  //Send a negative confirmation to the user application
882 }
883 
884 #endif
void * modbusClientGetResponsePdu(ModbusClientContext *context, size_t *length)
Retrieve response PDU.
uint8_t length
Definition: coap_common.h:193
#define htons(value)
Definition: cpu_endian.h:413
error_t modbusDumpRequestPdu(const void *pdu, size_t length)
Dump Modbus request PDU for debugging purpose.
Definition: modbus_debug.c:99
error_t modbusClientParseWriteMultipleRegsResp(ModbusClientContext *context, uint16_t address, uint_t quantity)
Parse Write Multiple Registers response.
Modbus/TCP client.
int bool_t
Definition: compiler_port.h:53
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 modbusClientParseExceptionResp(ModbusClientContext *context)
Parse Exception response.
__start_packed struct @14 ModbusWriteMultipleCoilsResp
Write Multiple Coils response PDU.
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.
__start_packed struct @20 ModbusReadWriteMultipleRegsResp
Read/Write Multiple Registers response PDU.
error_t modbusClientFormatMaskWriteRegReq(ModbusClientContext *context, uint16_t address, uint16_t andMask, uint16_t orMask)
Format Mask Write Register request.
__start_packed struct @18 ModbusMaskWriteRegResp
Mask Write Register response PDU.
__start_packed struct @8 ModbusReadInputRegsResp
Read Holding Input response PDU.
#define ModbusClientContext
Definition: modbus_client.h:86
@ MODBUS_COIL_STATE_ON
__start_packed struct @11 ModbusWriteSingleRegReq
Write Single Register request PDU.
error_t modbusClientFormatReadCoilsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Coils request.
@ MODBUS_FUNCTION_READ_DISCRETE_INPUTS
Definition: modbus_common.h:77
error_t modbusClientFormatReadInputRegsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Input Registers request.
error_t modbusClientFormatWriteSingleCoilReq(ModbusClientContext *context, uint16_t address, bool_t value)
Format Write Single Coil request.
__start_packed struct @5 ModbusReadHoldingRegsReq
Read Holding Registers request PDU.
#define osMemcpy(dest, src, length)
Definition: os_port.h:140
__start_packed struct @4 ModbusReadDiscreteInputsResp
Read Discrete Inputs response PDU.
@ MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS
Definition: modbus_common.h:92
error_t
Error codes.
Definition: error.h:43
__start_packed struct @9 ModbusWriteSingleCoilReq
Write Single Coil request PDU.
error_t modbusClientParseReadInputRegsResp(ModbusClientContext *context, uint_t quantity, uint16_t *value)
Parse Read Input Registers response.
__start_packed struct @12 ModbusWriteSingleRegResp
Write Single Register response PDU.
@ MODBUS_FUNCTION_WRITE_MULTIPLE_REGS
Definition: modbus_common.h:87
uint8_t value[]
Definition: tcp.h:367
error_t modbusClientParseReadDiscreteInputsResp(ModbusClientContext *context, uint_t quantity, uint8_t *value)
Parse Discrete Inputs response.
error_t modbusClientFormatMbapHeader(ModbusClientContext *context, size_t length)
Format MBAP header.
__start_packed struct @10 ModbusWriteSingleCoilResp
Write Single Coil response PDU.
__start_packed struct @13 ModbusWriteMultipleCoilsReq
Write Multiple Coils request PDU.
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ MODBUS_FUNCTION_MASK_WRITE_REG
Definition: modbus_common.h:91
__start_packed struct @7 ModbusReadInputRegsReq
Read Holding Input request PDU.
__start_packed struct @16 ModbusWriteMultipleRegsResp
Write Multiple Registers response PDU.
__start_packed struct @2 ModbusReadCoilsResp
Read Coils response PDU.
uint8_t mask
Definition: web_socket.h:317
@ MODBUS_FUNCTION_WRITE_SINGLE_REG
Definition: modbus_common.h:81
@ MODBUS_FUNCTION_READ_INPUT_REGS
Definition: modbus_common.h:79
__start_packed struct @3 ModbusReadDiscreteInputsReq
Read Discrete Inputs request PDU.
__start_packed struct @19 ModbusReadWriteMultipleRegsReq
Read/Write Multiple Registers request PDU.
__start_packed struct @21 ModbusExceptionResp
Exception response PDU.
Modbus PDU formatting and parsing.
@ ERROR_EXCEPTION_RECEIVED
Definition: error.h:204
#define ntohs(value)
Definition: cpu_endian.h:421
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint16_t orMask
__start_packed struct @15 ModbusWriteMultipleRegsReq
Write Multiple Registers request PDU.
Data logging functions for debugging purpose (Modbus/TCP)
#define HTONS(value)
Definition: cpu_endian.h:410
uint8_t n
void * modbusClientGetRequestPdu(ModbusClientContext *context)
Retrieve request PDU.
error_t modbusClientFormatWriteSingleRegReq(ModbusClientContext *context, uint16_t address, uint16_t value)
Format Write Single Register request.
__start_packed struct @17 ModbusMaskWriteRegReq
Mask Write Register request PDU.
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 modbusClientFormatReadDiscreteInputsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Discrete Inputs request.
@ MODBUS_FUNCTION_READ_HOLDING_REGS
Definition: modbus_common.h:78
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.
Ipv6Addr address
__start_packed struct @6 ModbusReadHoldingRegsResp
Read Holding Registers response PDU.
@ MODBUS_FUNCTION_WRITE_SINGLE_COIL
Definition: modbus_common.h:80
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
error_t modbusClientParseMaskWriteRegResp(ModbusClientContext *context, uint16_t address, uint16_t andMask, uint16_t orMask)
Parse Mask Write Register response.
ModbusExceptionCode
Modbus exception codes.
@ MODBUS_FUNCTION_WRITE_MULTIPLE_COILS
Definition: modbus_common.h:86
error_t modbusClientFormatReadHoldingRegsReq(ModbusClientContext *context, uint16_t address, uint_t quantity)
Format Read Holding Registers request.
Helper functions for Modbus/TCP client.
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
@ MODBUS_FUNCTION_READ_COILS
Definition: modbus_common.h:76
@ NO_ERROR
Success.
Definition: error.h:44
__start_packed struct @1 ModbusReadCoilsReq
Read Coils request PDU.
Debugging facilities.
@ MODBUS_COIL_STATE_OFF