forked from 4-20ma/ModbusMaster
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathModbusMaster.h
274 lines (215 loc) · 10.4 KB
/
ModbusMaster.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/**
@file
Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
@defgroup setup ModbusMaster Object Instantiation/Initialization
@defgroup buffer ModbusMaster Buffer Management
@defgroup discrete Modbus Function Codes for Discrete Coils/Inputs
@defgroup register Modbus Function Codes for Holding/Input Registers
@defgroup constant Modbus Function Codes, Exception Codes
*/
/*
ModbusMaster.h - Arduino library for communicating with Modbus slaves
over RS232/485 (via RTU protocol).
Library:: ModbusMaster
Copyright:: 2009-2016 Doc Walker
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef ModbusMaster_h
#define ModbusMaster_h
/**
@def __MODBUSMASTER_DEBUG__ (0)
Set to 1 to enable debugging features within class:
- PIN A cycles for each byte read in the Modbus response
- PIN B cycles for each millisecond timeout during the Modbus response
*/
#define __MODBUSMASTER_DEBUG__ (0)
#define __MODBUSMASTER_DEBUG_PIN_A__ 4
#define __MODBUSMASTER_DEBUG_PIN_B__ 5
/* _____STANDARD INCLUDES____________________________________________________ */
// include types & constants of Wiring core API
#include "mbed.h"
/* _____UTILITY MACROS_______________________________________________________ */
/* _____PROJECT INCLUDES_____________________________________________________ */
// functions to calculate Modbus Application Data Unit CRC
#include "util/crc16.h"
// functions to manipulate words
#include "util/word.h"
#include "util/bits.h"
/* _____CLASS DEFINITIONS____________________________________________________ */
/**
Arduino class library for communicating with Modbus slaves over
RS232/485 (via RTU protocol).
*/
class ModbusMaster
{
public:
ModbusMaster();
void begin(uint8_t, RawSerial &serial);
void idle(void (*)());
void preTransmission(void (*)());
void postTransmission(void (*)());
// Modbus exception codes
/**
Modbus protocol illegal function exception.
The function code received in the query is not an allowable action for
the server (or slave). This may be because the function code is only
applicable to newer devices, and was not implemented in the unit
selected. It could also indicate that the server (or slave) is in the
wrong state to process a request of this type, for example because it is
unconfigured and is being asked to return register values.
@ingroup constant
*/
static const uint8_t ku8MBIllegalFunction = 0x01;
/**
Modbus protocol illegal data address exception.
The data address received in the query is not an allowable address for
the server (or slave). More specifically, the combination of reference
number and transfer length is invalid. For a controller with 100
registers, the ADU addresses the first register as 0, and the last one
as 99. If a request is submitted with a starting register address of 96
and a quantity of registers of 4, then this request will successfully
operate (address-wise at least) on registers 96, 97, 98, 99. If a
request is submitted with a starting register address of 96 and a
quantity of registers of 5, then this request will fail with Exception
Code 0x02 "Illegal Data Address" since it attempts to operate on
registers 96, 97, 98, 99 and 100, and there is no register with address
100.
@ingroup constant
*/
static const uint8_t ku8MBIllegalDataAddress = 0x02;
/**
Modbus protocol illegal data value exception.
A value contained in the query data field is not an allowable value for
server (or slave). This indicates a fault in the structure of the
remainder of a complex request, such as that the implied length is
incorrect. It specifically does NOT mean that a data item submitted for
storage in a register has a value outside the expectation of the
application program, since the MODBUS protocol is unaware of the
significance of any particular value of any particular register.
@ingroup constant
*/
static const uint8_t ku8MBIllegalDataValue = 0x03;
/**
Modbus protocol slave device failure exception.
An unrecoverable error occurred while the server (or slave) was
attempting to perform the requested action.
@ingroup constant
*/
static const uint8_t ku8MBSlaveDeviceFailure = 0x04;
// Class-defined success/exception codes
/**
ModbusMaster success.
Modbus transaction was successful; the following checks were valid:
- slave ID
- function code
- response code
- data
- CRC
@ingroup constant
*/
static const uint8_t ku8MBSuccess = 0x00;
/**
ModbusMaster invalid response slave ID exception.
The slave ID in the response does not match that of the request.
@ingroup constant
*/
static const uint8_t ku8MBInvalidSlaveID = 0xE0;
/**
ModbusMaster invalid response function exception.
The function code in the response does not match that of the request.
@ingroup constant
*/
static const uint8_t ku8MBInvalidFunction = 0xE1;
/**
ModbusMaster response timed out exception.
The entire response was not received within the timeout period,
ModbusMaster::ku8MBResponseTimeout.
@ingroup constant
*/
static const uint8_t ku8MBResponseTimedOut = 0xE2;
/**
ModbusMaster invalid response CRC exception.
The CRC in the response does not match the one calculated.
@ingroup constant
*/
static const uint8_t ku8MBInvalidCRC = 0xE3;
uint16_t getResponseBuffer(uint8_t);
void clearResponseBuffer();
uint8_t setTransmitBuffer(uint8_t, uint16_t);
void clearTransmitBuffer();
void beginTransmission(uint16_t);
uint8_t requestFrom(uint16_t, uint16_t);
void sendBit(bool);
void send(uint8_t);
void send(uint16_t);
void send(uint32_t);
uint8_t available(void);
uint16_t receive(void);
uint8_t readCoils(uint16_t, uint16_t);
uint8_t readDiscreteInputs(uint16_t, uint16_t);
uint8_t readHoldingRegisters(uint16_t, uint16_t);
uint8_t readInputRegisters(uint16_t, uint8_t);
uint8_t writeSingleCoil(uint16_t, uint8_t);
uint8_t writeSingleRegister(uint16_t, uint16_t);
uint8_t writeMultipleCoils(uint16_t, uint16_t);
uint8_t writeMultipleCoils();
uint8_t writeMultipleRegisters(uint16_t, uint16_t);
uint8_t writeMultipleRegisters();
uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t);
uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t);
uint8_t readWriteMultipleRegisters(uint16_t, uint16_t);
private:
RawSerial * _serial; ///< reference to serial port object
uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in begin()
static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers
uint16_t _u16ReadAddress; ///< slave register from which to read
uint16_t _u16ReadQty; ///< quantity of words to read
uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer()
uint16_t _u16WriteAddress; ///< slave register to which to write
uint16_t _u16WriteQty; ///< quantity of words to write
uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer()
uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx
uint8_t _u8TransmitBufferIndex;
uint16_t u16TransmitBufferLength;
uint16_t* rxBuffer; // from Wire.h -- need to clean this up Rx
uint8_t _u8ResponseBufferIndex;
uint8_t _u8ResponseBufferLength;
bool _txInProgress;
// Modbus function codes for bit access
static const uint8_t ku8MBReadCoils = 0x01; ///< Modbus function 0x01 Read Coils
static const uint8_t ku8MBReadDiscreteInputs = 0x02; ///< Modbus function 0x02 Read Discrete Inputs
static const uint8_t ku8MBWriteSingleCoil = 0x05; ///< Modbus function 0x05 Write Single Coil
static const uint8_t ku8MBWriteMultipleCoils = 0x0F; ///< Modbus function 0x0F Write Multiple Coils
// Modbus function codes for 16 bit access
static const uint8_t ku8MBReadHoldingRegisters = 0x03; ///< Modbus function 0x03 Read Holding Registers
static const uint8_t ku8MBReadInputRegisters = 0x04; ///< Modbus function 0x04 Read Input Registers
static const uint8_t ku8MBWriteSingleRegister = 0x06; ///< Modbus function 0x06 Write Single Register
static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers
static const uint8_t ku8MBMaskWriteRegister = 0x16; ///< Modbus function 0x16 Mask Write Register
static const uint8_t ku8MBReadWriteMultipleRegisters = 0x17; ///< Modbus function 0x17 Read Write Multiple Registers
// Modbus timeout [milliseconds]
static const uint16_t ku16MBResponseTimeout = 2000; ///< Modbus timeout [milliseconds]
// master function that conducts Modbus transactions
uint8_t ModbusMasterTransaction(uint8_t u8MBFunction);
// idle callback function; gets called during idle time between TX and RX
void (*_idle)();
// preTransmission callback function; gets called before writing a Modbus message
void (*_preTransmission)();
// postTransmission callback function; gets called after a Modbus message has been sent
void (*_postTransmission)();
void txDone(void);
};
#endif
/**
@example examples/Basic/Basic.pde
@example examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde
@example examples/RS485_HalfDuplex/RS485_HalfDuplex.ino
*/