liblightmodbus 3.0
A lightweight, header-only, hardware-agnostic Modbus RTU/TCP library
Loading...
Searching...
No Matches
slave_func.impl.h
Go to the documentation of this file.
1#ifndef LIGHTMODBUS_SLAVE_FUNC_IMPL_H
2#define LIGHTMODBUS_SLAVE_FUNC_IMPL_H
3
4#include "slave_func.h"
5#include "slave.h"
6
22 ModbusSlave *status,
23 uint8_t function,
24 const uint8_t *requestPDU,
25 uint8_t requestLength)
26{
27 // Check frame length
28 if (requestLength != 5)
29 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
30
31 ModbusDataType datatype;
32 uint16_t maxCount;
33 uint8_t isCoilType;
34 switch (function)
35 {
36 case 1:
37 datatype = MODBUS_COIL;
38 maxCount = 2000;
39 isCoilType = 1;
40 break;
41
42 case 2:
43 datatype = MODBUS_DISCRETE_INPUT;
44 maxCount = 2000;
45 isCoilType = 1;
46 break;
47
48 case 3:
49 datatype = MODBUS_HOLDING_REGISTER;
50 maxCount = 125;
51 isCoilType = 0;
52 break;
53
54 case 4:
55 datatype = MODBUS_INPUT_REGISTER;
56 maxCount = 125;
57 isCoilType = 0;
58 break;
59
60 default:
61 return MODBUS_GENERAL_ERROR(FUNCTION);
62 break;
63 }
64
65 uint16_t index = modbusRBE(&requestPDU[1]);
66 uint16_t count = modbusRBE(&requestPDU[3]);
67
68 // Check count
69 if (count == 0 || count > maxCount)
70 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
71
72 // Addresss range check
73 if (modbusCheckRangeU16(index, count))
74 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_ADDRESS);
75
76 // Prepare callback args
79 .type = datatype,
80 .query = MODBUS_REGQ_R_CHECK,
81 .index = 0,
82 .value = 0,
83 .function = function,
84 };
85
86 // Check if all registers can be read
87 for (uint16_t i = 0; i < count; i++)
88 {
89 cargs.index = index + i;
90 ModbusError fail = status->registerCallback(status, &cargs, &cres);
91 if (fail) return modbusBuildException(status, function, MODBUS_EXCEP_SLAVE_FAILURE);
92 if (cres.exceptionCode) return modbusBuildException(status, function, cres.exceptionCode);
93 }
94
95 // ---- RESPONSE ----
96
97 uint8_t dataLength = (isCoilType ? modbusBitsToBytes(count) : (count << 1));
98 if (modbusSlaveAllocateResponse(status, 2 + dataLength))
99 return MODBUS_GENERAL_ERROR(ALLOC);
100
101 status->response.pdu[0] = function;
102 status->response.pdu[1] = dataLength;
103
104 // Clear with zeros, if we're writing bits
105 for (uint8_t i = 0; i < dataLength; i++)
106 status->response.pdu[2 + i] = 0;
107
108 cargs.query = MODBUS_REGQ_R;
109 for (uint16_t i = 0; i < count; i++)
110 {
111 cargs.index = index + i;
112 (void) status->registerCallback(status, &cargs, &cres);
113
114 if (isCoilType)
115 modbusMaskWrite(&status->response.pdu[2], i, cres.value != 0);
116 else
117 modbusWBE(&status->response.pdu[2 + (i << 1)], cres.value);
118 }
119
120 return MODBUS_NO_ERROR();
121}
122
132 ModbusSlave *status,
133 uint8_t function,
134 const uint8_t *requestPDU,
135 uint8_t requestLength)
136{
137 // Check frame length
138 if (requestLength != 5)
139 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
140
141 // Get register index and value
142 ModbusDataType datatype = function == 5 ? MODBUS_COIL : MODBUS_HOLDING_REGISTER;
143 uint16_t index = modbusRBE(&requestPDU[1]);
144 uint16_t value = modbusRBE(&requestPDU[3]);
145
146 // For coils - check if coil value is valid
147 if (datatype == MODBUS_COIL && value != 0x0000 && value != 0xFF00)
148 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
149
150 // Prepare callback args
153 .type = datatype,
154 .query = MODBUS_REGQ_W_CHECK,
155 .index = index,
156 .value = (uint16_t)((datatype == MODBUS_COIL) ? (value != 0) : value),
157 .function = function,
158 };
159
160 // Check if the register/coil can be written
161 ModbusError fail = status->registerCallback(status, &cargs, &cres);
162 if (fail) return modbusBuildException(status, function, MODBUS_EXCEP_SLAVE_FAILURE);
163 if (cres.exceptionCode) return modbusBuildException(status, function, cres.exceptionCode);
164
165 // Write coil/register
166 // Keep in mind that 0xff00 is 0 when cast to uint8_t
167 cargs.query = MODBUS_REGQ_W;
168 (void) status->registerCallback(status, &cargs, &cres);
169
170 // ---- RESPONSE ----
171
172 if (modbusSlaveAllocateResponse(status, 5))
173 return MODBUS_GENERAL_ERROR(ALLOC);
174
175 status->response.pdu[0] = function;
176 modbusWBE(&status->response.pdu[1], index);
177 modbusWBE(&status->response.pdu[3], value);
178
179 return MODBUS_NO_ERROR();
180}
181
191 ModbusSlave *status,
192 uint8_t function,
193 const uint8_t *requestPDU,
194 uint8_t requestLength)
195{
196 // Check length
197 if (requestLength < 6)
198 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
199
200 // Get first index and register count
201 ModbusDataType datatype = function == 15 ? MODBUS_COIL : MODBUS_HOLDING_REGISTER;
202 uint16_t maxCount = datatype == MODBUS_COIL ? 1968 : 123;
203 uint16_t index = modbusRBE(&requestPDU[1]);
204 uint16_t count = modbusRBE(&requestPDU[3]);
205 uint8_t declaredLength = requestPDU[5];
206
207 // Check if the declared length is correct
208 if (declaredLength == 0 || declaredLength != requestLength - 6)
209 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
210
211 // Check count
212 if (count == 0
213 || count > maxCount
214 || declaredLength != (datatype == MODBUS_COIL ? modbusBitsToBytes(count) : (count << 1)))
215 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
216
217 // Addresss range check
218 if (modbusCheckRangeU16(index, count))
219 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_ADDRESS);
220
221 // Prepare callback args
224 .type = datatype,
225 .query = MODBUS_REGQ_W_CHECK,
226 .index = 0,
227 .value = 0,
228 .function = function,
229 };
230
231 // Check write access
232 for (uint16_t i = 0; i < count; i++)
233 {
234 cargs.index = index + i;
235 cargs.value = datatype == MODBUS_COIL ? modbusMaskRead(&requestPDU[6], i) : modbusRBE(&requestPDU[6 + (i << 1)]);
236 ModbusError fail = status->registerCallback(status, &cargs, &cres);
237 if (fail) return modbusBuildException(status, function, MODBUS_EXCEP_SLAVE_FAILURE);
238 if (cres.exceptionCode) return modbusBuildException(status, function, cres.exceptionCode);
239 }
240
241 // Write coils
242 cargs.query = MODBUS_REGQ_W;
243 for (uint16_t i = 0; i < count; i++)
244 {
245 cargs.index = index + i;
246 cargs.value = datatype == MODBUS_COIL ? modbusMaskRead(&requestPDU[6], i) : modbusRBE(&requestPDU[6 + (i << 1)]);
247 (void) status->registerCallback(status, &cargs, &cres);
248 }
249
250 // ---- RESPONSE ----
251
252 if (modbusSlaveAllocateResponse(status, 5))
253 return MODBUS_GENERAL_ERROR(ALLOC);
254
255 status->response.pdu[0] = function;
256 modbusWBE(&status->response.pdu[1], index);
257 modbusWBE(&status->response.pdu[3], count);
258
259 return MODBUS_NO_ERROR();
260}
261
271 ModbusSlave *status,
272 uint8_t function,
273 const uint8_t *requestPDU,
274 uint8_t requestLength)
275{
276 // Check length
277 if (requestLength != 7)
278 return modbusBuildException(status, function, MODBUS_EXCEP_ILLEGAL_VALUE);
279
280 // Get index and masks
281 uint16_t index = modbusRBE(&requestPDU[1]);
282 uint16_t andmask = modbusRBE(&requestPDU[3]);
283 uint16_t ormask = modbusRBE(&requestPDU[5]);
284
285 // Prepare callback args
289 .query = MODBUS_REGQ_R_CHECK,
290 .index = index,
291 .value = 0,
292 .function = function,
293 };
294
295 // Check read access
297 ModbusError fail = status->registerCallback(status, &cargs, &cres);
298 if (fail) return modbusBuildException(status, function, MODBUS_EXCEP_SLAVE_FAILURE);
299 if (cres.exceptionCode) return modbusBuildException(status, function, cres.exceptionCode);
300
301 // Read the register
302 cargs.query = MODBUS_REGQ_R;
303 (void) status->registerCallback(status, &cargs, &cres);
304 uint16_t value = cres.value;
305
306 // Compute new value for the register
307 value = (value & andmask) | (ormask & ~andmask);
308
309 // Check write access
311 cargs.value = value;
312 fail = status->registerCallback(status, &cargs, &cres);
313 if (fail) return modbusBuildException(status, function, MODBUS_EXCEP_SLAVE_FAILURE);
314 if (cres.exceptionCode) return modbusBuildException(status, function, cres.exceptionCode);
315
316 // Write the register
317 cargs.query = MODBUS_REGQ_W;
318 (void) status->registerCallback(status, &cargs, &cres);
319
320 // ---- RESPONSE ----
321
322 if (modbusSlaveAllocateResponse(status, 7))
323 return MODBUS_GENERAL_ERROR(ALLOC);
324
325 status->response.pdu[0] = function;
326 modbusWBE(&status->response.pdu[1], index);
327 modbusWBE(&status->response.pdu[3], andmask);
328 modbusWBE(&status->response.pdu[5], ormask);
329
330 return MODBUS_NO_ERROR();
331}
332
333#endif
@ MODBUS_EXCEP_ILLEGAL_ADDRESS
Illegal data address.
Definition base.h:233
@ MODBUS_EXCEP_SLAVE_FAILURE
Slave could not process the request.
Definition base.h:235
@ MODBUS_EXCEP_ILLEGAL_VALUE
Illegal data value.
Definition base.h:234
ModbusDataType
Represents different Modbus data types.
Definition base.h:244
@ MODBUS_DISCRETE_INPUT
Discrete input.
Definition base.h:248
@ MODBUS_HOLDING_REGISTER
Holding register.
Definition base.h:245
@ MODBUS_INPUT_REGISTER
Input register.
Definition base.h:246
@ MODBUS_COIL
Coil.
Definition base.h:247
ModbusError
Represtents different kinds of errors.
Definition base.h:137
#define MODBUS_NO_ERROR()
Construcs a ModbusErrorInfo object for which modbusIsOK() is guaranteed to return true.
Definition base.h:86
static void modbusMaskWrite(uint8_t *mask, uint16_t n, uint8_t value)
Writes n-th bit in an array.
Definition base.h:344
static uint8_t modbusCheckRangeU16(uint16_t index, uint16_t count)
Checks whether provided address range causes an uint16_t overflow.
Definition base.h:408
static uint16_t modbusWBE(uint8_t *p, uint16_t val)
Safely writes a big-endian 16-bit word to provided pointer.
Definition base.h:395
#define LIGHTMODBUS_RET_ERROR
Return type for library functions returning ModbusErrorInfo that should be handled properly.
Definition base.h:49
#define MODBUS_GENERAL_ERROR(e)
Constructs a ModbusErrorInfo where source is set to MODBUS_ERROR_SOURCE_GENERAL and the error code is...
Definition base.h:93
static uint16_t modbusRBE(const uint8_t *p)
Safely reads a big-endian 16-bit word from provided pointer.
Definition base.h:385
static uint16_t modbusBitsToBytes(uint16_t n)
Returns number of bytes necessary to hold given number of bits.
Definition base.h:357
static uint8_t modbusMaskRead(const uint8_t *mask, uint16_t n)
Reads n-th bit from an array.
Definition base.h:333
Slave's types and basic functions (header)
ModbusErrorInfo modbusBuildException(ModbusSlave *status, uint8_t function, ModbusExceptionCode code)
Builds an exception response frame.
Definition slave.impl.h:118
static ModbusError modbusSlaveAllocateResponse(ModbusSlave *status, uint16_t pduSize)
Allocates memory for slave's response frame.
Definition slave.h:184
@ MODBUS_REGQ_W_CHECK
Request for write access.
Definition slave.h:39
@ MODBUS_REGQ_W
Write request.
Definition slave.h:41
@ MODBUS_REGQ_R_CHECK
Request for read access.
Definition slave.h:38
@ MODBUS_REGQ_R
Read request.
Definition slave.h:40
Slave's functions for parsing requests (header)
ModbusErrorInfo modbusParseRequest1516(ModbusSlave *status, uint8_t function, const uint8_t *requestPDU, uint8_t requestLength)
Handles requests 15 and 16 (Write Multiple XX) and generates response.
Definition slave_func.impl.h:190
ModbusErrorInfo modbusParseRequest0506(ModbusSlave *status, uint8_t function, const uint8_t *requestPDU, uint8_t requestLength)
Handles requests 05 and 06 (Write Single XX) and generates response.
Definition slave_func.impl.h:131
ModbusErrorInfo modbusParseRequest22(ModbusSlave *status, uint8_t function, const uint8_t *requestPDU, uint8_t requestLength)
Handles request 22 (Mask Write Register) and generates response.
Definition slave_func.impl.h:270
ModbusErrorInfo modbusParseRequest01020304(ModbusSlave *status, uint8_t function, const uint8_t *requestPDU, uint8_t requestLength)
Handles requests 01, 02, 03 and 04 (Read Multiple XX) and generates response.
Definition slave_func.impl.h:21
uint8_t * pdu
A pointer to the PDU section of the frame.
Definition base.h:281
Contains arguments for the register callback function.
Definition slave.h:48
uint16_t value
Value of the register.
Definition slave.h:52
ModbusDataType type
Type of accessed data.
Definition slave.h:49
ModbusRegisterQuery query
Type of request made to the register.
Definition slave.h:50
uint16_t index
Index of the register.
Definition slave.h:51
Contains values returned by the slave register callback.
Definition slave.h:60
uint16_t value
Register/coil value.
Definition slave.h:62
ModbusExceptionCode exceptionCode
Exception to be reported.
Definition slave.h:61
Slave device status.
Definition slave.h:90
ModbusRegisterCallback registerCallback
A pointer to register callback (required)
Definition slave.h:91
ModbusBuffer response
Stores slave's response to master.
Definition slave.h:97