1 /*
2 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
3 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * File: $Id: mbfunccoils_m.c,v 1.60 2013/10/12 15:10:12 Armink Add Master Functions
29 */
30
31 /* ----------------------- System includes ----------------------------------*/
32 #include "stdlib.h"
33 #include "string.h"
34
35 /* ----------------------- Platform includes --------------------------------*/
36 #include "port.h"
37
38 /* ----------------------- Modbus includes ----------------------------------*/
39 #include "mb_m.h"
40 #include "mbframe.h"
41 #include "mbproto.h"
42 #include "mbconfig.h"
43 #include "mbutils.h"
44
45 /* ----------------------- Defines ------------------------------------------*/
46 #define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
47 #define MB_PDU_REQ_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
48 #define MB_PDU_REQ_READ_SIZE ( 4 )
49 #define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 0 )
50 #define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
51 #define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
52
53 #define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
54 #define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
55 #define MB_PDU_REQ_WRITE_SIZE ( 4 )
56 #define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
57 #define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
58 #define MB_PDU_FUNC_WRITE_SIZE ( 4 )
59
60 #define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
61 #define MB_PDU_REQ_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
62 #define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
63 #define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
64 #define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
65 #define MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
66 #define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
67 #define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
68 #define MB_PDU_FUNC_WRITE_MUL_SIZE ( 5 )
69
70 /* ----------------------- Static functions ---------------------------------*/
71 eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
72
73 /* ----------------------- Start implementation -----------------------------*/
74 #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
75
76 #if MB_FUNC_READ_COILS_ENABLED
77
78 /**
79 * This function will request read coil.
80 *
81 * @param ucSndAddr salve address
82 * @param usCoilAddr coil start address
83 * @param usNCoils coil total number
84 * @param lTimeOut timeout (-1 will waiting forever)
85 *
86 * @return error code
87 */
88 eMBMasterReqErrCode
eMBMasterReqReadCoils(UCHAR ucSndAddr,USHORT usCoilAddr,USHORT usNCoils,LONG lTimeOut)89 eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut )
90 {
91 UCHAR *ucMBFrame;
92 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
93
94 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
95 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
96 else
97 {
98 vMBMasterGetPDUSndBuf(&ucMBFrame);
99 vMBMasterSetDestAddress(ucSndAddr);
100 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_COILS;
101 ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usCoilAddr >> 8;
102 ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usCoilAddr;
103 ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8;
104 ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils;
105 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
106 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
107 eErrStatus = eMBMasterWaitRequestFinish( );
108
109 }
110 return eErrStatus;
111 }
112
113 eMBException
eMBMasterFuncReadCoils(UCHAR * pucFrame,USHORT * usLen)114 eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
115 {
116 UCHAR *ucMBFrame;
117 USHORT usRegAddress;
118 USHORT usCoilCount;
119 UCHAR ucByteCount;
120
121 eMBException eStatus = MB_EX_NONE;
122 eMBErrorCode eRegStatus;
123
124 /* If this request is broadcast, and it's read mode. This request don't need execute. */
125 if ( xMBMasterRequestIsBroadcast() )
126 {
127 eStatus = MB_EX_NONE;
128 }
129 else if ( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
130 {
131 vMBMasterGetPDUSndBuf(&ucMBFrame);
132 usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
133 usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
134 usRegAddress++;
135
136 usCoilCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF] << 8 );
137 usCoilCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] );
138
139 /* Test if the quantity of coils is a multiple of 8. If not last
140 * byte is only partially field with unused coils set to zero. */
141 if( ( usCoilCount & 0x0007 ) != 0 )
142 {
143 ucByteCount = ( UCHAR )( usCoilCount / 8 + 1 );
144 }
145 else
146 {
147 ucByteCount = ( UCHAR )( usCoilCount / 8 );
148 }
149
150 /* Check if the number of registers to read is valid. If not
151 * return Modbus illegal data value exception.
152 */
153 if( ( usCoilCount >= 1 ) &&
154 ( ucByteCount == pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] ) )
155 {
156 /* Make callback to fill the buffer. */
157 eRegStatus = eMBMasterRegCoilsCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usCoilCount, MB_REG_READ );
158
159 /* If an error occurred convert it into a Modbus exception. */
160 if( eRegStatus != MB_ENOERR )
161 {
162 eStatus = prveMBError2Exception( eRegStatus );
163 }
164 }
165 else
166 {
167 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
168 }
169 }
170 else
171 {
172 /* Can't be a valid read coil register request because the length
173 * is incorrect. */
174 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
175 }
176 return eStatus;
177 }
178 #endif
179
180 #if MB_FUNC_WRITE_COIL_ENABLED > 0
181
182 /**
183 * This function will request write one coil.
184 *
185 * @param ucSndAddr salve address
186 * @param usCoilAddr coil start address
187 * @param usCoilData data to be written
188 * @param lTimeOut timeout (-1 will waiting forever)
189 *
190 * @return error code
191 *
192 * @see eMBMasterReqWriteMultipleCoils
193 */
194 eMBMasterReqErrCode
eMBMasterReqWriteCoil(UCHAR ucSndAddr,USHORT usCoilAddr,USHORT usCoilData,LONG lTimeOut)195 eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut )
196 {
197 UCHAR *ucMBFrame;
198 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
199
200 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
201 else if ( ( usCoilData != 0xFF00 ) && ( usCoilData != 0x0000 ) ) eErrStatus = MB_MRE_ILL_ARG;
202 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
203 else
204 {
205 vMBMasterGetPDUSndBuf(&ucMBFrame);
206 vMBMasterSetDestAddress(ucSndAddr);
207 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_SINGLE_COIL;
208 ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usCoilAddr >> 8;
209 ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usCoilAddr;
210 ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8;
211 ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData;
212 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
213 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
214 eErrStatus = eMBMasterWaitRequestFinish( );
215 }
216 return eErrStatus;
217 }
218
219 eMBException
eMBMasterFuncWriteCoil(UCHAR * pucFrame,USHORT * usLen)220 eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
221 {
222 USHORT usRegAddress;
223 UCHAR ucBuf[2];
224
225 eMBException eStatus = MB_EX_NONE;
226 eMBErrorCode eRegStatus;
227
228 if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
229 {
230 usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
231 usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
232 usRegAddress++;
233
234 if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
235 ( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
236 ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
237 {
238 ucBuf[1] = 0;
239 if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
240 {
241 ucBuf[0] = 1;
242 }
243 else
244 {
245 ucBuf[0] = 0;
246 }
247 eRegStatus =
248 eMBMasterRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
249
250 /* If an error occured convert it into a Modbus exception. */
251 if( eRegStatus != MB_ENOERR )
252 {
253 eStatus = prveMBError2Exception( eRegStatus );
254 }
255 }
256 else
257 {
258 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
259 }
260 }
261 else
262 {
263 /* Can't be a valid write coil register request because the length
264 * is incorrect. */
265 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
266 }
267 return eStatus;
268 }
269
270 #endif // #if MB_FUNC_WRITE_COIL_ENABLED > 0
271
272 #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
273
274 /**
275 * This function will request write multiple coils.
276 *
277 * @param ucSndAddr salve address
278 * @param usCoilAddr coil start address
279 * @param usNCoils coil total number
280 * @param usCoilData data to be written
281 * @param lTimeOut timeout (-1 will waiting forever)
282 *
283 * @return error code
284 *
285 * @see eMBMasterReqWriteCoil
286 */
287 eMBMasterReqErrCode
eMBMasterReqWriteMultipleCoils(UCHAR ucSndAddr,USHORT usCoilAddr,USHORT usNCoils,UCHAR * pucDataBuffer,LONG lTimeOut)288 eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
289 USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut)
290 {
291 UCHAR *ucMBFrame;
292 USHORT usRegIndex = 0;
293 UCHAR ucByteCount;
294 eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
295
296 if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
297 else if ( usNCoils > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ) eErrStatus = MB_MRE_ILL_ARG;
298 else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
299 else
300 {
301 vMBMasterGetPDUSndBuf(&ucMBFrame);
302 vMBMasterSetDestAddress(ucSndAddr);
303 ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_COILS;
304 ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usCoilAddr >> 8;
305 ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usCoilAddr;
306 ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF] = usNCoils >> 8;
307 ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF + 1] = usNCoils ;
308 if( ( usNCoils & 0x0007 ) != 0 )
309 {
310 ucByteCount = ( UCHAR )( usNCoils / 8 + 1 );
311 }
312 else
313 {
314 ucByteCount = ( UCHAR )( usNCoils / 8 );
315 }
316 ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = ucByteCount;
317 ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
318 while( ucByteCount > usRegIndex)
319 {
320 *ucMBFrame++ = pucDataBuffer[usRegIndex++];
321 }
322 vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount );
323 ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
324 eErrStatus = eMBMasterWaitRequestFinish( );
325 }
326 return eErrStatus;
327 }
328
329 eMBException
eMBMasterFuncWriteMultipleCoils(UCHAR * pucFrame,USHORT * usLen)330 eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
331 {
332 USHORT usRegAddress;
333 USHORT usCoilCnt;
334 UCHAR ucByteCount;
335 UCHAR ucByteCountVerify;
336 UCHAR *ucMBFrame;
337
338 eMBException eStatus = MB_EX_NONE;
339 eMBErrorCode eRegStatus;
340
341 /* If this request is broadcast, the *usLen is not need check. */
342 if( ( *usLen == MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
343 {
344 vMBMasterGetPDUSndBuf(&ucMBFrame);
345 usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
346 usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
347 usRegAddress++;
348
349 usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
350 usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
351
352 ucByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
353
354 /* Compute the number of expected bytes in the request. */
355 if( ( usCoilCnt & 0x0007 ) != 0 )
356 {
357 ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
358 }
359 else
360 {
361 ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
362 }
363
364 if( ( usCoilCnt >= 1 ) && ( ucByteCountVerify == ucByteCount ) )
365 {
366 eRegStatus =
367 eMBMasterRegCoilsCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
368 usRegAddress, usCoilCnt, MB_REG_WRITE );
369
370 /* If an error occured convert it into a Modbus exception. */
371 if( eRegStatus != MB_ENOERR )
372 {
373 eStatus = prveMBError2Exception( eRegStatus );
374 }
375 }
376 else
377 {
378 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
379 }
380 }
381 else
382 {
383 /* Can't be a valid write coil register request because the length
384 * is incorrect. */
385 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
386 }
387 return eStatus;
388 }
389
390 #endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
391 #endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
392