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-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"
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  {
210  request->outputValue = HTONS(MODBUS_COIL_STATE_ON);
211  }
212  else
213  {
214  request->outputValue = HTONS(MODBUS_COIL_STATE_OFF);
215  }
216 
217  //Compute the length of the request PDU
219 
220  //Debug message
221  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
222  //Dump the contents of the PDU for debugging purpose
223  modbusDumpRequestPdu(request, length);
224 
225  //Format MBAP header
226  return modbusClientFormatMbapHeader(context, length);
227 }
228 
229 
230 /**
231  * @brief Format Write Single Register request
232  * @param[in] context Pointer to the Modbus/TCP client context
233  * @param[in] address Address of the register to be written
234  * @param[in] value Register value
235  * @return Error code
236  **/
237 
239  uint16_t address, uint16_t value)
240 {
241  size_t length;
242  ModbusWriteSingleRegReq *request;
243 
244  //Point to the Modbus request PDU
245  request = modbusClientGetRequestPdu(context);
246 
247  //Format Write Single Register request
248  request->functionCode = MODBUS_FUNCTION_WRITE_SINGLE_REG;
249  request->regAddr = htons(address);
250  request->regValue = htons(value);
251 
252  //Compute the length of the request PDU
254 
255  //Debug message
256  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
257  //Dump the contents of the PDU for debugging purpose
258  modbusDumpRequestPdu(request, length);
259 
260  //Format MBAP header
261  return modbusClientFormatMbapHeader(context, length);
262 }
263 
264 
265 /**
266  * @brief Format Write Multiple Coils request
267  * @param[in] context Pointer to the Modbus/TCP client context
268  * @param[in] address Address of the first coil to be forced
269  * @param[in] quantity Number of coils
270  * @param[in] value Value of the discrete outputs
271  * @return Error code
272  **/
273 
275  uint16_t address, uint_t quantity, const uint8_t *value)
276 {
277  uint8_t mask;
278  size_t length;
280 
281  //Point to the Modbus request PDU
282  request = modbusClientGetRequestPdu(context);
283 
284  //Format Write Multiple Coils request
285  request->functionCode = MODBUS_FUNCTION_WRITE_MULTIPLE_COILS;
286  request->startingAddr = htons(address);
287  request->quantityOfOutputs = htons(quantity);
288  request->byteCount = (quantity + 7) / 8;
289 
290  //Copy coil values
291  osMemcpy(request->outputValue, value, request->byteCount);
292 
293  //Unused bits in the last data byte should be zero–filled
294  if((quantity % 8) != 0)
295  {
296  mask = (1 << (quantity % 8)) - 1;
297  request->outputValue[request->byteCount - 1] &= mask;
298  }
299 
300  //Compute the length of the request PDU
301  length = sizeof(ModbusWriteMultipleCoilsReq) + request->byteCount;
302 
303  //Debug message
304  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
305  //Dump the contents of the PDU for debugging purpose
306  modbusDumpRequestPdu(request, length);
307 
308  //Format MBAP header
309  return modbusClientFormatMbapHeader(context, length);
310 }
311 
312 
313 /**
314  * @brief Format Write Multiple Registers request
315  * @param[in] context Pointer to the Modbus/TCP client context
316  * @param[in] address Starting register address
317  * @param[in] quantity Number of registers
318  * @param[in] value Value of the holding registers
319  * @return Error code
320  **/
321 
323  uint16_t address, uint_t quantity, const uint16_t *value)
324 {
325  uint_t i;
326  size_t length;
328 
329  //Point to the Modbus request PDU
330  request = modbusClientGetRequestPdu(context);
331 
332  //Format Write Multiple Registers request
333  request->functionCode = MODBUS_FUNCTION_WRITE_MULTIPLE_REGS;
334  request->startingAddr = htons(address);
335  request->quantityOfRegs = htons(quantity);
336  request->byteCount = quantity * sizeof(uint16_t);
337 
338  //Copy register values
339  for(i = 0; i < quantity; i++)
340  {
341  request->regValue[i] = ntohs(value[i]);
342  }
343 
344  //Compute the length of the request PDU
345  length = sizeof(ModbusWriteMultipleRegsReq) + request->byteCount;
346 
347  //Debug message
348  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
349  //Dump the contents of the PDU for debugging purpose
350  modbusDumpRequestPdu(request, length);
351 
352  //Format MBAP header
353  return modbusClientFormatMbapHeader(context, length);
354 }
355 
356 
357 /**
358  * @brief Format Mask Write Register request
359  * @param[in] context Pointer to the Modbus/TCP client context
360  * @param[in] address Address of the holding register
361  * @param[in] andMask AND bitmask
362  * @param[in] orMask OR bitmask
363  * @return Error code
364  **/
365 
367  uint16_t address, uint16_t andMask, uint16_t orMask)
368 {
369  size_t length;
370  ModbusMaskWriteRegReq *request;
371 
372  //Point to the Modbus request PDU
373  request = modbusClientGetRequestPdu(context);
374 
375  //Format Write Single Register request
376  request->functionCode = MODBUS_FUNCTION_MASK_WRITE_REG;
377  request->referenceAddr = htons(address);
378  request->andMask = htons(andMask);
379  request->orMask = htons(orMask);
380 
381  //Compute the length of the request PDU
382  length = sizeof(ModbusMaskWriteRegReq);
383 
384  //Debug message
385  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
386  //Dump the contents of the PDU for debugging purpose
387  modbusDumpRequestPdu(request, length);
388 
389  //Format MBAP header
390  return modbusClientFormatMbapHeader(context, length);
391 }
392 
393 
394 /**
395  * @brief Format Read/Write Multiple Registers request
396  * @param[in] context Pointer to the Modbus/TCP client context
397  * @param[in] readAddress Address of the first holding registers to be read
398  * @param[in] readQuantity Number of holding registers to be read
399  * @param[in] writeAddress Address of the first holding registers to be written
400  * @param[in] writeQuantity Number of holding registers to be written
401  * @param[in] writeValue Value of the holding registers (write operation)
402  * @return Error code
403  **/
404 
406  uint16_t readAddress, uint16_t readQuantity, uint16_t writeAddress,
407  uint16_t writeQuantity, const uint16_t *writeValue)
408 {
409  uint_t i;
410  size_t length;
412 
413  //Point to the Modbus request PDU
414  request = modbusClientGetRequestPdu(context);
415 
416  //Format Read/Write Multiple Registers request
417  request->functionCode = MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS;
418  request->readStartingAddr = htons(readAddress);
419  request->quantityToRead = htons(readQuantity);
420  request->writeStartingAddr = htons(writeAddress);
421  request->quantityToWrite = htons(writeQuantity);
422  request->writeByteCount = writeQuantity * sizeof(uint16_t);
423 
424  //Copy register values
425  for(i = 0; i < writeQuantity; i++)
426  {
427  request->writeRegValue[i] = ntohs(writeValue[i]);
428  }
429 
430  //Compute the length of the request PDU
431  length = sizeof(ModbusReadWriteMultipleRegsReq) + request->writeByteCount;
432 
433  //Debug message
434  TRACE_DEBUG("\r\nModbus Client: Sending Request PDU (%" PRIuSIZE " bytes)...\r\n", length);
435  //Dump the contents of the PDU for debugging purpose
436  modbusDumpRequestPdu(request, length);
437 
438  //Format MBAP header
439  return modbusClientFormatMbapHeader(context, length);
440 }
441 
442 
443 /**
444  * @brief Parse Read Coils response
445  * @param[in] context Pointer to the Modbus/TCP client context
446  * @param[in] quantity Number of coils
447  * @param[out] value Value of the discrete outputs
448  * @return Error code
449  **/
450 
452  uint_t quantity, uint8_t *value)
453 {
454  size_t n;
455  size_t length;
456  ModbusReadCoilsResp *response;
457 
458  //Point to the Modbus response PDU
459  response = modbusClientGetResponsePdu(context, &length);
460 
461  //Malformed PDU?
462  if(length < sizeof(ModbusReadCoilsResp))
463  return ERROR_INVALID_LENGTH;
464 
465  //Compute the length of the data field
466  n = length - sizeof(ModbusReadCoilsResp);
467 
468  //Check function code
469  if(response->functionCode != MODBUS_FUNCTION_READ_COILS)
470  return ERROR_INVALID_RESPONSE;
471 
472  //Check byte count field
473  if(response->byteCount != n || response->byteCount != ((quantity + 7) / 8))
474  return ERROR_INVALID_LENGTH;
475 
476  //Copy coil values
477  osMemcpy(value, response->coilStatus, response->byteCount);
478 
479  //Successful processing
480  return NO_ERROR;
481 }
482 
483 
484 /**
485  * @brief Parse Discrete Inputs response
486  * @param[in] context Pointer to the Modbus/TCP client context
487  * @param[in] quantity Number of inputs
488  * @param[out] value Value of the discrete inputs
489  * @return Error code
490  **/
491 
493  uint_t quantity, uint8_t *value)
494 {
495  size_t n;
496  size_t length;
498 
499  //Point to the Modbus response PDU
500  response = modbusClientGetResponsePdu(context, &length);
501 
502  //Malformed PDU?
504  return ERROR_INVALID_LENGTH;
505 
506  //Compute the length of the data field
508 
509  //Check function code
510  if(response->functionCode != MODBUS_FUNCTION_READ_DISCRETE_INPUTS)
511  return ERROR_INVALID_RESPONSE;
512 
513  //Check byte count field
514  if(response->byteCount != n || response->byteCount != ((quantity + 7) / 8))
515  return ERROR_INVALID_LENGTH;
516 
517  //Copy discrete input values
518  osMemcpy(value, response->inputStatus, response->byteCount);
519 
520  //Successful processing
521  return NO_ERROR;
522 }
523 
524 
525 /**
526  * @brief Parse Read Holding Registers response
527  * @param[in] context Pointer to the Modbus/TCP client context
528  * @param[in] quantity Number of registers
529  * @param[out] value Value of the holding registers
530  * @return Error code
531  **/
532 
534  uint_t quantity, uint16_t *value)
535 {
536  uint_t i;
537  size_t n;
538  size_t length;
539  ModbusReadHoldingRegsResp *response;
540 
541  //Point to the Modbus response PDU
542  response = modbusClientGetResponsePdu(context, &length);
543 
544  //Malformed PDU?
545  if(length < sizeof(ModbusReadHoldingRegsResp))
546  return ERROR_INVALID_LENGTH;
547 
548  //Compute the length of the data field
549  n = length - sizeof(ModbusReadHoldingRegsResp);
550 
551  //Check function code
552  if(response->functionCode != MODBUS_FUNCTION_READ_HOLDING_REGS)
553  return ERROR_INVALID_RESPONSE;
554 
555  //Check byte count field
556  if(response->byteCount != n ||
557  response->byteCount != (quantity * sizeof(uint16_t)))
558  {
559  return ERROR_INVALID_LENGTH;
560  }
561 
562  //Copy register values
563  for(i = 0; i < quantity; i++)
564  {
565  value[i] = ntohs(response->regValue[i]);
566  }
567 
568  //Successful processing
569  return NO_ERROR;
570 }
571 
572 
573 /**
574  * @brief Parse Read Input Registers response
575  * @param[in] context Pointer to the Modbus/TCP client context
576  * @param[in] quantity Number of registers
577  * @param[out] value Value of the input registers
578  * @return Error code
579  **/
580 
582  uint_t quantity, uint16_t *value)
583 {
584  uint_t i;
585  size_t n;
586  size_t length;
587  ModbusReadInputRegsResp *response;
588 
589  //Point to the Modbus response PDU
590  response = modbusClientGetResponsePdu(context, &length);
591 
592  //Malformed PDU?
593  if(length < sizeof(ModbusReadInputRegsResp))
594  return ERROR_INVALID_LENGTH;
595 
596  //Compute the length of the data field
597  n = length - sizeof(ModbusReadInputRegsResp);
598 
599  //Check function code
600  if(response->functionCode != MODBUS_FUNCTION_READ_INPUT_REGS)
601  return ERROR_INVALID_RESPONSE;
602 
603  //Check byte count field
604  if(response->byteCount != n ||
605  response->byteCount != (quantity * sizeof(uint16_t)))
606  {
607  return ERROR_INVALID_LENGTH;
608  }
609 
610  //Copy register values
611  for(i = 0; i < quantity; i++)
612  {
613  value[i] = ntohs(response->regValue[i]);
614  }
615 
616  //Successful processing
617  return NO_ERROR;
618 }
619 
620 
621 /**
622  * @brief Parse Write Single Coil response
623  * @param[in] context Pointer to the Modbus/TCP client context
624  * @param[in] address Address of the coil to be forced
625  * @param[in] value Value of the discrete output
626  * @return Error code
627  **/
628 
630  uint16_t address, bool_t value)
631 {
632  size_t length;
633  bool_t referenceValue;
634  ModbusWriteSingleCoilResp *response;
635 
636  //Point to the Modbus response PDU
637  response = modbusClientGetResponsePdu(context, &length);
638 
639  //Malformed PDU?
640  if(length < sizeof(ModbusWriteSingleCoilResp))
641  return ERROR_INVALID_LENGTH;
642 
643  //Check function code
644  if(response->functionCode != MODBUS_FUNCTION_WRITE_SINGLE_COIL)
645  return ERROR_INVALID_RESPONSE;
646 
647  //A value of 0xFF00 requests the output to be ON. A value of 0x0000
648  //requests it to be OFF
649  referenceValue = value ? MODBUS_COIL_STATE_ON : MODBUS_COIL_STATE_OFF;
650 
651  //The normal response is an echo of the request
652  if(ntohs(response->outputAddr) != address ||
653  ntohs(response->outputValue) != referenceValue)
654  {
655  return ERROR_INVALID_RESPONSE;
656  }
657 
658  //Successful processing
659  return NO_ERROR;
660 }
661 
662 
663 /**
664  * @brief Parse Write Single Register response
665  * @param[in] context Pointer to the Modbus/TCP client context
666  * @param[in] address Address of the register to be written
667  * @param[in] value Register value
668  * @return Error code
669  **/
670 
672  uint16_t address, uint16_t value)
673 {
674  size_t length;
675  ModbusWriteSingleRegResp *response;
676 
677  //Point to the Modbus response PDU
678  response = modbusClientGetResponsePdu(context, &length);
679 
680  //Malformed PDU?
681  if(length < sizeof(ModbusWriteSingleRegResp))
682  return ERROR_INVALID_LENGTH;
683 
684  //Check function code
685  if(response->functionCode != MODBUS_FUNCTION_WRITE_SINGLE_REG)
686  return ERROR_INVALID_RESPONSE;
687 
688  //The normal response is an echo of the request
689  if(ntohs(response->regAddr) != address ||
690  ntohs(response->regValue) != value)
691  {
692  return ERROR_INVALID_RESPONSE;
693  }
694 
695  //Successful processing
696  return NO_ERROR;
697 }
698 
699 
700 /**
701  * @brief Parse Write Multiple Coils response
702  * @param[in] context Pointer to the Modbus/TCP client context
703  * @param[in] address Address of the first coil to be forced
704  * @param[in] quantity Number of coils
705  * @return Error code
706  **/
707 
709  uint16_t address, uint_t quantity)
710 {
711  size_t length;
713 
714  //Point to the Modbus response PDU
715  response = modbusClientGetResponsePdu(context, &length);
716 
717  //Malformed PDU?
719  return ERROR_INVALID_LENGTH;
720 
721  //Check function code
722  if(response->functionCode != MODBUS_FUNCTION_WRITE_MULTIPLE_COILS)
723  return ERROR_INVALID_RESPONSE;
724 
725  //The normal response returns the starting address, and quantity of
726  //coils forced
727  if(ntohs(response->startingAddr) != address ||
728  ntohs(response->quantityOfOutputs) != quantity)
729  {
730  return ERROR_INVALID_RESPONSE;
731  }
732 
733  //Successful processing
734  return NO_ERROR;
735 }
736 
737 
738 /**
739  * @brief Parse Write Multiple Registers response
740  * @param[in] context Pointer to the Modbus/TCP client context
741  * @param[in] address Starting register address
742  * @param[in] quantity Number of registers
743  * @return Error code
744  **/
745 
747  uint16_t address, uint_t quantity)
748 {
749  size_t length;
750  ModbusWriteMultipleRegsResp *response;
751 
752  //Point to the Modbus response PDU
753  response = modbusClientGetResponsePdu(context, &length);
754 
755  //Malformed PDU?
756  if(length < sizeof(ModbusWriteMultipleRegsResp))
757  return ERROR_INVALID_LENGTH;
758 
759  //Check function code
760  if(response->functionCode != MODBUS_FUNCTION_WRITE_MULTIPLE_REGS)
761  return ERROR_INVALID_RESPONSE;
762 
763  //The normal response returns the starting address, and quantity of
764  //registers written
765  if(ntohs(response->startingAddr) != address ||
766  ntohs(response->quantityOfRegs) != quantity)
767  {
768  return ERROR_INVALID_RESPONSE;
769  }
770 
771  //Successful processing
772  return NO_ERROR;
773 }
774 
775 
776 /**
777  * @brief Parse Mask Write Register response
778  * @param[in] context Pointer to the Modbus/TCP client context
779  * @param[in] address Address of the holding register
780  * @param[in] andMask AND bitmask
781  * @param[in] orMask OR bitmask
782  * @return Error code
783  **/
784 
786  uint16_t address, uint16_t andMask, uint16_t orMask)
787 {
788  size_t length;
789  ModbusMaskWriteRegResp *response;
790 
791  //Point to the Modbus response PDU
792  response = modbusClientGetResponsePdu(context, &length);
793 
794  //Malformed PDU?
795  if(length < sizeof(ModbusMaskWriteRegResp))
796  return ERROR_INVALID_LENGTH;
797 
798  //Check function code
799  if(response->functionCode != MODBUS_FUNCTION_MASK_WRITE_REG)
800  return ERROR_INVALID_RESPONSE;
801 
802  //The normal response is an echo of the request
803  if(ntohs(response->referenceAddr) != address ||
804  ntohs(response->andMask) != andMask ||
805  ntohs(response->orMask) != orMask)
806  {
807  return ERROR_INVALID_RESPONSE;
808  }
809 
810  //Successful processing
811  return NO_ERROR;
812 }
813 
814 
815 /**
816  * @brief Parse Read/Write Multiple Registers response
817  * @param[in] context Pointer to the Modbus/TCP client context
818  * @param[in] readQuantity Number of holding registers to be read
819  * @param[out] readValue Value of the holding registers
820  * @return Error code
821  **/
822 
824  uint_t readQuantity, uint16_t *readValue)
825 {
826  uint_t i;
827  size_t n;
828  size_t length;
830 
831  //Point to the Modbus response PDU
832  response = modbusClientGetResponsePdu(context, &length);
833 
834  //Malformed PDU?
836  return ERROR_INVALID_LENGTH;
837 
838  //Compute the length of the data field
839  n = length - sizeof(ModbusReadInputRegsResp);
840 
841  //Check function code
842  if(response->functionCode != MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS)
843  return ERROR_INVALID_RESPONSE;
844 
845  //Check byte count field
846  if(response->readByteCount != n ||
847  response->readByteCount != (readQuantity * sizeof(uint16_t)))
848  {
849  return ERROR_INVALID_LENGTH;
850  }
851 
852  //Copy register values
853  for(i = 0; i < readQuantity; i++)
854  {
855  readValue[i] = ntohs(response->readRegValue[i]);
856  }
857 
858  //Successful processing
859  return NO_ERROR;
860 }
861 
862 
863 /**
864  * @brief Parse Exception response
865  * @param[in] context Pointer to the Modbus/TCP client context
866  * @return Error code
867  **/
868 
870 {
871  size_t length;
872  ModbusExceptionResp *response;
873 
874  //Point to the Modbus response PDU
875  response = modbusClientGetResponsePdu(context, &length);
876 
877  //Malformed PDU?
878  if(length < sizeof(ModbusExceptionResp))
879  return ERROR_INVALID_LENGTH;
880 
881  //Save exception code
882  context->exceptionCode = (ModbusExceptionCode) response->exceptionCode;
883 
884  //Send a negative confirmation to the user application
886 }
887 
888 #endif
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
int bool_t
Definition: compiler_port.h:53
#define HTONS(value)
Definition: cpu_endian.h:410
#define htons(value)
Definition: cpu_endian.h:413
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_EXCEPTION_RECEIVED
Definition: error.h:204
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ ERROR_INVALID_RESPONSE
Definition: error.h:71
Ipv6Addr address[]
Definition: ipv6.h:316
Modbus/TCP client.
#define ModbusClientContext
Definition: modbus_client.h:86
void * modbusClientGetResponsePdu(ModbusClientContext *context, size_t *length)
Retrieve response PDU.
error_t modbusClientFormatMbapHeader(ModbusClientContext *context, size_t length)
Format MBAP header.
void * modbusClientGetRequestPdu(ModbusClientContext *context)
Retrieve request PDU.
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 modbusClientParseExceptionResp(ModbusClientContext *context)
Parse Exception 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.
ModbusExceptionCode
Modbus exception codes.
ModbusReadInputRegsReq
ModbusWriteMultipleCoilsResp
ModbusExceptionResp
ModbusReadDiscreteInputsReq
ModbusWriteSingleRegReq
ModbusWriteMultipleRegsResp
@ MODBUS_FUNCTION_WRITE_SINGLE_COIL
Definition: modbus_common.h:80
@ MODBUS_FUNCTION_READ_COILS
Definition: modbus_common.h:76
@ MODBUS_FUNCTION_WRITE_SINGLE_REG
Definition: modbus_common.h:81
@ MODBUS_FUNCTION_READ_WRITE_MULTIPLE_REGS
Definition: modbus_common.h:92
@ MODBUS_FUNCTION_WRITE_MULTIPLE_REGS
Definition: modbus_common.h:87
@ MODBUS_FUNCTION_READ_HOLDING_REGS
Definition: modbus_common.h:78
@ MODBUS_FUNCTION_WRITE_MULTIPLE_COILS
Definition: modbus_common.h:86
@ MODBUS_FUNCTION_READ_INPUT_REGS
Definition: modbus_common.h:79
@ MODBUS_FUNCTION_MASK_WRITE_REG
Definition: modbus_common.h:91
@ MODBUS_FUNCTION_READ_DISCRETE_INPUTS
Definition: modbus_common.h:77
ModbusReadWriteMultipleRegsResp
ModbusWriteSingleCoilReq
ModbusReadHoldingRegsReq
ModbusReadDiscreteInputsResp
ModbusReadCoilsResp
ModbusWriteMultipleCoilsReq
ModbusWriteSingleCoilResp
ModbusReadWriteMultipleRegsReq
ModbusWriteSingleRegResp
uint16_t orMask
@ MODBUS_COIL_STATE_ON
@ MODBUS_COIL_STATE_OFF
ModbusMaskWriteRegResp
uint16_t andMask
ModbusReadInputRegsResp
ModbusReadHoldingRegsResp
ModbusReadCoilsReq
ModbusMaskWriteRegReq
ModbusWriteMultipleRegsReq
error_t modbusDumpRequestPdu(const void *pdu, size_t length)
Dump Modbus request PDU for debugging purpose.
Definition: modbus_debug.c:99
Data logging functions for debugging purpose (Modbus/TCP)
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
uint8_t length
Definition: tcp.h:368
uint8_t value[]
Definition: tcp.h:369
uint8_t mask
Definition: web_socket.h:319