├── LICENCE
├── README.md
├── README_CN.md
├── examples
├── modifyModbusID
│ └── modifyModbusID.ino
└── scanModbusID
│ └── scanModbusID.ino
├── keywords.txt
├── library.properties
├── python
└── raspberrypi
│ ├── DFRobot_RTU.py
│ ├── README.md
│ ├── README_CN.md
│ └── examples
│ ├── modify_modbus_id.py
│ ├── scan_modbus_id.py
│ └── test.py
└── src
├── DFRobot_RTU.cpp
└── DFRobot_RTU.h
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright 2010 DFRobot Co.Ltd
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DFRobot_RTU
2 |
3 | * [中文版](./README_CN.md)
4 |
5 | Modbus RTU library for Arduino. The supported modbus commands are as follows:
6 | * 0x01: Read one or multiple coils register;
7 | * 0x02: Read one or multiple discrete inputs register;
8 | * 0x03: Read one or multiple holding register;
9 | * 0x04: Read one or multiple input register;
10 | * 0x05: Write a coils register;
11 | * 0x06: write a holding register;
12 | * 0x0F: Write multiple coils register;
13 | * 0x10: Write multiple holding register;
14 |
15 | 
16 |
17 |
18 | ## Product Link(链接到英文商城)
19 |
20 |
21 | ## Table of Contents
22 |
23 | * [Summary](#summary)
24 | * [Connected](#connected)
25 | * [Installation](#installation)
26 | * [Calibration](#calibration)
27 | * [Methods](#methods)
28 | * [Compatibility](#compatibility)
29 | * [History](#history)
30 | * [Credits](#credits)
31 |
32 | ## Summary
33 | This is a modbus RTU libary for Arduino by DFRobot.
34 |
35 | ## Connected
36 | Hardware conneted table
37 |
38 | Sensor | MCU |
39 | ------------ | :-------------------------------: |
40 | VCC | 5V |
41 | GND | GND |
42 | RX |connected to the UART TX pin of MCU|
43 | TX |connected to the UART RX pin of MCU|
44 |
45 | ## Installation
46 |
47 | To use this library, first download the library file, paste it into the \Arduino\libraries directory, then open the examples folder and run the demo in the folder.
48 |
49 | ## Methods
50 |
51 | ```C++
52 | /**
53 | * @brief DFRobot_RTU abstract class constructor. Construct serial port.
54 | * @param s: The class pointer object of Abstract class, here you can fill in the pointer to the serial port object.
55 | * @param dePin: RS485 flow control, pull low to receive, pull high to send.
56 | */
57 | DFRobot_RTU(Stream *s,int dePin);
58 | DFRobot_RTU(Stream *s);
59 | ~DFRobot_RTU()
60 |
61 | /**
62 | * @brief Set receive timeout time, unit ms.
63 | * @param timeout: receive timeout time, unit ms, default 100mss.
64 | */
65 | void setTimeoutTimeMs(uint32_t timeout = 100);
66 |
67 | /**
68 | * @brief Read a coils Register.
69 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
70 | * @n but will not answer.
71 | * @param reg: Coils register address.
72 | * @return Return the value of the coils register value.
73 | * @n true: The value of the coils register value is 1.
74 | * @n false: The value of the coils register value is 0.
75 | */
76 | bool readCoilsRegister(uint8_t id, uint16_t reg);
77 |
78 | /**
79 | * @brief Read a discrete input register.
80 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
81 | * @n but will not answer.
82 | * @param reg: Discrete input register address.
83 | * @return Return the value of the discrete input register.
84 | */
85 | uint16_t readDiscreteInputsRegister(uint8_t id, uint16_t reg);
86 |
87 | /**
88 | * @brief Read a holding Register.
89 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
90 | * @n but will not answer.
91 | * @param reg: Holding register address.
92 | * @return Return the value of the holding register value.
93 | */
94 | uint16_t readHoldingRegister(uint8_t id, uint16_t reg);
95 |
96 | /**
97 | * @brief Read a input Register.
98 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
99 | * @n but will not answer.
100 | * @param reg: input register address.
101 | * @return Return the value of the input register value.
102 | */
103 | uint16_t readInputRegister(uint8_t id, uint16_t reg);
104 |
105 | /**
106 | * @brief Write a coils Register.
107 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
108 | * @n but will not answer.
109 | * @param reg: Coils register address.
110 | * @param flag: The value of the register value which will be write, 0 ro 1.
111 | * @return Exception code:
112 | * @n 0 : sucess.
113 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
114 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
115 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
116 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
117 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
118 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
119 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
120 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
121 | */
122 | uint8_t writeCoilsRegister(uint8_t id, uint16_t reg, bool flag);
123 |
124 | /**
125 | * @brief Write a holding register.
126 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
127 | * @n but will not answer.
128 | * @param reg: Holding register address.
129 | * @return Exception code:
130 | * @n 0 : sucess.
131 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
132 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
133 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
134 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
135 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
136 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
137 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
138 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
139 | */
140 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t val);
141 |
142 | /**
143 | * @brief Read multiple coils Register.
144 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
145 | * @n but will not answer.
146 | * @param reg: Coils register address.
147 | * @param regNum: Number of coils Register.
148 | * @param data: Storage register worth pointer.
149 | * @param size: Cache size of data.
150 | * @return Exception code:
151 | * @n 0 : sucess.
152 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
153 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
154 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
155 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
156 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
157 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
158 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
159 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
160 | */
161 | uint8_t readCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size);
162 |
163 | /**
164 | * @brief Read multiple discrete inputs register.
165 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
166 | * @n but will not answer.
167 | * @param reg: Discrete inputs register. address.
168 | * @param regNum: Number of coils Register.
169 | * @param data: Storage register worth pointer.
170 | * @param size: Cache size.
171 | * @return Exception code:
172 | * @n 0 : sucess.
173 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
174 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
175 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
176 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
177 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
178 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
179 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
180 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
181 | */
182 | uint8_t readDiscreteInputsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size);
183 |
184 | /**
185 | * @brief Read multiple Holding register.
186 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
187 | * @n but will not answer.
188 | * @param reg: Holding register.
189 | * @param data: Storage register worth pointer.
190 | * @param size: Cache size.
191 | * @return Exception code:
192 | * @n 0 : sucess.
193 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
194 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
195 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
196 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
197 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
198 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
199 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
200 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
201 | */
202 | uint8_t readHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
203 |
204 | /**
205 | * @brief Read multiple Input register.
206 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
207 | * @n but will not answer.
208 | * @param reg: Input register.
209 | * @param data: Storage register worth pointer.
210 | * @param regNum: register numbers.
211 | * @return Exception code:
212 | * @n 0 : sucess.
213 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
214 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
215 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
216 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
217 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
218 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
219 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
220 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
221 | */
222 | uint8_t readInputRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
223 |
224 | /**
225 | * @brief Read multiple Holding register.
226 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
227 | * @n but will not answer.
228 | * @param reg: Holding register.
229 | * @param data: Storage register worth pointer.
230 | * @param regNum: register numbers.
231 | * @return Exception code:
232 | * @n 0 : sucess.
233 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
234 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
235 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
236 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
237 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
238 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
239 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
240 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
241 | */
242 | uint8_t readHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum);
243 |
244 | /**
245 | * @brief Read multiple Input register.
246 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
247 | * @n but will not answer.
248 | * @param reg: Input register.
249 | * @param data: Storage register worth pointer.
250 | * @param regNum: register numbers.
251 | * @return Exception code:
252 | * @n 0 : sucess.
253 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
254 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
255 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
256 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
257 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
258 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
259 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
260 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
261 | */
262 | uint8_t DFRobot_RTU::readInputRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t size)
263 |
264 | /**
265 | * @brief Write multiple coils Register.
266 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
267 | * @n but will not answer.
268 | * @param reg: Coils register address.
269 | * @param regNum: Numbers of Coils register.
270 | * @param data: Storage register worth pointer.
271 | * @param size: Cache size.
272 | * @return Exception code:
273 | * @n 0 : sucess.
274 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
275 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
276 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
277 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
278 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
279 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
280 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
281 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
282 | */
283 | uint8_t writeCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, void *data, uint16_t size);
284 |
285 | /**
286 | * @brief Write multiple Holding Register.
287 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
288 | * @n but will not answer.
289 | * @param reg: Holding register address.
290 | * @param data: Storage register worth pointer.
291 | * @param size: Cache size.
292 | * @return Exception code:
293 | * @n 0 : sucess.
294 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
295 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
296 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
297 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
298 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
299 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
300 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
301 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
302 | */
303 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
304 |
305 | /**
306 | * @brief Write multiple Holding Register.
307 | * @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
308 | * @n but will not answer.
309 | * @param reg: Holding register address.
310 | * @param data: Storage register worth pointer.
311 | * @param regNum: Number of coils Register..
312 | * @return Exception code:
313 | * @n 0 : sucess.
314 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
315 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
316 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
317 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
318 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
319 | * @n 9 or eRTU_RECV_ERROR: Receive packet error.
320 | * @n 10 or eRTU_MEMORY_ERROR: Memory error.
321 | * @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
322 | */
323 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum);
324 | ```
325 |
326 | ## Compatibility
327 |
328 | MCU | SoftwareSerial | HardwareSerial |
329 | ------------------ | :----------: | :----------: |
330 | Arduino Uno | √ | X |
331 | Mega2560 | √ | √ |
332 | Leonardo | √ | √ |
333 | ESP32 | X | √ |
334 | ESP8266 | √ | X |
335 | micro:bit | X | X |
336 | FireBeetle M0 | X | √ |
337 | raspberry | X | √ |
338 |
339 | ## History
340 |
341 | - Data 2021-07-17
342 | - Version V1.0
343 |
344 | ## Credits
345 |
346 | Written by(xue.peng@dfrobot.com), 2021. (Welcome to our [website](https://www.dfrobot.com/))
347 |
348 |
349 |
350 |
351 |
352 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # DFRobot_RTU
2 |
3 | * [English Version](./README.md)
4 |
5 | 这是一个基于Modbus RTU协议的Arduino modbus库,它支持以下几种modbus协议命令:
6 | * 0x01: 读一个或多个线圈寄存器;
7 | * 0x02: 读一个或多个离散输入寄存器;
8 | * 0x03: 读一个或多个保持寄存器;
9 | * 0x04: 读一个或多个输入寄存器;
10 | * 0x05: 写单个线圈寄存器;
11 | * 0x06: 写单个保持寄存器;
12 | * 0x0F: 写多个线圈寄存器;
13 | * 0x10: 写多个保持寄存器;
14 |
15 | 
16 |
17 |
18 | ## Product Link(链接到英文商城)
19 |
20 |
21 | ## Table of Contents
22 |
23 | * [Summary](#summary)
24 | * [Connected](#connected)
25 | * [Installation](#installation)
26 | * [Calibration](#calibration)
27 | * [Methods](#methods)
28 | * [Compatibility](#compatibility)
29 | * [History](#history)
30 | * [Credits](#credits)
31 |
32 | ## Summary
33 | 这是DFRobot基于modbus RTU协议为Arduino平台移植的modbus库。
34 |
35 | ## Connected
36 | Hardware conneted table
37 |
38 | Sensor | MCU |
39 | ------------ | :-------------------------------: |
40 | VCC | 5V |
41 | GND | GND |
42 | RX |connected to the UART TX pin of MCU|
43 | TX |connected to the UART RX pin of MCU|
44 |
45 | ## Installation
46 |
47 | To use this library, first download the library file, paste it into the \Arduino\libraries directory, then open the examples folder and run the demo in the folder.
48 |
49 | ## Methods
50 |
51 | ```C++
52 | /**
53 | * @brief DFRobot_RTU构造函数. 传递串口指针.
54 | * @param s: 串口抽象类指针。
55 | * @param dePin RS485流控引脚,拉低接收,拉高发送
56 | */
57 | DFRobot_RTU(Stream *s,int dePin);
58 | DFRobot_RTU(Stream *s);
59 | ~DFRobot_RTU()
60 |
61 | /**
62 | * @brief 设置串口接收超时时间,单位ms.
63 | * @param timeout: 串口接收超时时间参数,单位ms,默认100ms
64 | */
65 | void setTimeoutTimeMs(uint32_t timeout = 100);
66 |
67 | /**
68 | * @brief 读取线圈寄存器的值。
69 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
70 | * @param reg: 线圈寄存器地址.
71 | * @return 返回线圈寄存器的值。
72 | * @n true: 线圈寄存器的值为1
73 | * @n false: 线圈寄存器的值为0
74 | */
75 | bool readCoilsRegister(uint8_t id, uint16_t reg);
76 |
77 | /**
78 | * @brief 读取离散输入寄存器的值。
79 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
80 | * @param reg: 离散输入寄存器地址.
81 | * @return 返回离散输入寄存器的值。
82 | */
83 | uint16_t readDiscreteInputsRegister(uint8_t id, uint16_t reg);
84 |
85 | /**
86 | * @brief 读取保持寄存器的值。
87 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
88 | * @param reg: 保持寄存器地址.
89 | * @return 返回保持寄存器的值。
90 | */
91 | uint16_t readHoldingRegister(uint8_t id, uint16_t reg);
92 |
93 | /**
94 | * @brief 读取输入寄存器的值。
95 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
96 | * @param reg: 输入寄存器地址.
97 | * @return 返回输入寄存器的值。
98 | */
99 | uint16_t readInputRegister(uint8_t id, uint16_t reg);
100 |
101 | /**
102 | * @brief 写单个线圈寄存器的值。
103 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
104 | * @param reg: 线圈寄存器地址.
105 | * @param flag: 要写入的线圈寄存器的值,0 或 1。
106 | * @return 返回写线圈寄存起的值, 0 或 1。
107 | */
108 | uint8_t writeCoilsRegister(uint8_t id, uint16_t reg, bool flag);
109 |
110 | /**
111 | * @brief 写单个保持寄存器的值。
112 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
113 | * @param reg: 保持寄存器地址.
114 | * @param val: 要写入的保持寄存器的值。
115 | * @return Exception code:
116 | * @n 0 : sucess.
117 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
118 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
119 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
120 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
121 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
122 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
123 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
124 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
125 | */
126 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t val);
127 |
128 | /**
129 | * @brief 读多个线圈寄存器的值。
130 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
131 | * @param reg: 读线圈寄存器的起始地址.
132 | * @param regNum: 线圈寄存器的个数
133 | * @param data: 存储要读取的数据的指针.
134 | * @param size: 要读取的字节数.
135 | * @return Exception code:
136 | * @n 0 : sucess.
137 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
138 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
139 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
140 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
141 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
142 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
143 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
144 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
145 | */
146 | uint8_t readCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size);
147 |
148 | /**
149 | * @brief 读多个离散输入寄存器的值。
150 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
151 | * @param reg: 读离散输入寄存器的起始地址.
152 | * @param regNum: 离散寄存器的个数
153 | * @param data: 存储要读取的数据的指针.
154 | * @param size: 要读取的字节数.
155 | * @return Exception code:
156 | * @n 0 : sucess.
157 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
158 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
159 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
160 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
161 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
162 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
163 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
164 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
165 | */
166 | uint8_t readDiscreteInputsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size);
167 |
168 | /**
169 | * @brief 读多个保持寄存器的值。
170 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
171 | * @param reg: 读保持寄存器的起始地址.
172 | * @param data: 存储要读取的数据的指针.
173 | * @param size: 要读取的字节数.
174 | * @return Exception code:
175 | * @n 0 : sucess.
176 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
177 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
178 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
179 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
180 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
181 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
182 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
183 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
184 | */
185 | uint8_t readHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
186 |
187 |
188 | /**
189 | * @brief 读多个输入寄存器的值。
190 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
191 | * @param reg: 读输入寄存器的起始地址.
192 | * @param data: 存储要读取的数据的指针.
193 | * @param size: 要读取的字节数.
194 | * @return Exception code:
195 | * @n 0 : sucess.
196 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
197 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
198 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
199 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
200 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
201 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
202 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
203 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
204 | */
205 | uint8_t readInputRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
206 |
207 | /**
208 | * @brief 读多个保持寄存器的值。
209 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
210 | * @param reg: 读保持寄存器的起始地址.
211 | * @param data: 存储要读取的数据的指针.
212 | * @param regNum: 表示要读取的寄存器的个数.
213 | * @return Exception code:
214 | * @n 0 : sucess.
215 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
216 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
217 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
218 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
219 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
220 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
221 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
222 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
223 | */
224 | uint8_t readHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum);
225 |
226 | /**
227 | * @brief 读多个输入寄存器的值。
228 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
229 | * @param reg: 读输入寄存器的起始地址.
230 | * @param data: 存储要读取的数据的指针.
231 | * @param size: 要读取的字节数.
232 | * @return Exception code:
233 | * @n 0 : sucess.
234 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
235 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
236 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
237 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
238 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
239 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
240 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
241 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
242 | */
243 | uint8_t readInputRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t size);
244 |
245 | /**
246 | * @brief 写多个线圈寄存器的值。
247 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
248 | * @param reg: 写线圈寄存器的起始地址.
249 | * @param regNum: 线圈寄存器的个数.
250 | * @param data: 存储要写入的数据的指针.
251 | * @param size: 要写入的字节数.
252 | * @return Exception code:
253 | * @n 0 : sucess.
254 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
255 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
256 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
257 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
258 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
259 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
260 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
261 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
262 | */
263 | uint8_t writeCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, void *data, uint16_t size);
264 |
265 | /**
266 | * @brief 写多个保持寄存器的值。
267 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
268 | * @param reg: 写保持寄存器的起始地址.
269 | * @param data: 存储要写入的数据的指针.
270 | * @param size: 要写入的字节数.
271 | * @return Exception code:
272 | * @n 0 : sucess.
273 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
274 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
275 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
276 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
277 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
278 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
279 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
280 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
281 | */
282 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size);
283 |
284 | /**
285 | * @brief 写多个保持寄存器的值。
286 | * @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
287 | * @param reg: 写保持寄存器的起始地址.
288 | * @param data: 存储要写入的数据的指针.
289 | * @param regNum: 表示要读取的寄存器的个数.
290 | * @return Exception code:
291 | * @n 0 : sucess.
292 | * @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
293 | * @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
294 | * @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
295 | * @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
296 | * @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
297 | * @n 9 or eRTU_RECV_ERROR: 接收包错误.
298 | * @n 10 or eRTU_MEMORY_ERROR: 内存错误.
299 | * @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
300 | */
301 | uint8_t writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum);
302 | ```
303 |
304 | ## Compatibility
305 |
306 | MCU | SoftwareSerial | HardwareSerial |
307 | ------------------ | :----------: | :----------: |
308 | Arduino Uno | √ | X |
309 | Mega2560 | √ | √ |
310 | Leonardo | √ | √ |
311 | ESP32 | X | √ |
312 | ESP8266 | √ | X |
313 | micro:bit | X | X |
314 | FireBeetle M0 | X | √ |
315 | raspberry | X | √ |
316 |
317 | ## History
318 |
319 | - Data 2021-07-17
320 | - Version V1.0
321 |
322 | ## Credits
323 |
324 | Written by(xue.peng@dfrobot.com), 2021. (Welcome to our [website](https://www.dfrobot.com/))
325 |
--------------------------------------------------------------------------------
/examples/modifyModbusID/modifyModbusID.ino:
--------------------------------------------------------------------------------
1 | /*!
2 | * @file modifyModbusID.ino
3 | * @brief 修改modbus从机的设备ID。每个modbus从机都有唯一识别的设备ID号,范围0x0000~0x00F7(0~247),修改设备ID有2种方式:
4 | * @n 1: 不知道设备的ID地址,可以通过广播地址0x00修改从机的ID地址,此命令会将总线上所有的从机的地址都修改为设置的ID(用0x00修改地址时,总线上最好只接一个设备)
5 | * @n 2: 知道设备的ID地址,直接用ID修改
6 | * @n note:运行此demo必须知道设备的串口配置(波特率,数据位,校验位,停止位)
7 | * @n connected table
8 | * ---------------------------------------------------------------------------------------------------------------
9 | * sensor pin | MCU | Leonardo/Mega2560/M0 | UNO | ESP8266 | ESP32 | microbit |
10 | * VCC | 3.3V/5V | VCC | VCC | VCC | VCC | X |
11 | * GND | GND | GND | GND | GND | GND | X |
12 | * RX | TX | Serial1 RX1 | 5 |5/D6(TX) | D2 | X |
13 | * TX | RX | Serial1 TX1 | 4 |4/D7(RX) | D3 | X |
14 | * ---------------------------------------------------------------------------------------------------------------
15 | *
16 | *
17 | * @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
18 | * @licence The MIT License (MIT)
19 | * @author [Arya](xue.peng@dfrobot.com)
20 | * @version V1.0
21 | * @date 2021-07-16
22 | * @https://github.com/DFRobot/DFRobot_RTU
23 | */
24 | #include "DFRobot_RTU.h"
25 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
26 | #include
27 | #endif
28 |
29 | #define DEVICE_ID_REG 0x02
30 |
31 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
32 | SoftwareSerial mySerial(/*rx =*/4, /*tx =*/5);
33 | DFRobot_RTU modbus(/*s =*/&mySerial);
34 | #else
35 | DFRobot_RTU modbus(/*s =*/&Serial1);
36 | #endif
37 |
38 | void setup() {
39 | Serial.begin(115200);
40 | while(!Serial){ //Waiting for USB Serial COM port to open.
41 | }
42 |
43 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
44 | mySerial.begin(9600);
45 | #elif defined(ESP32)
46 | Serial1.begin(9600, SERIAL_8N1, /*rx =*/D3, /*tx =*/D2);
47 | #else
48 | Serial1.begin(9600);
49 | #endif
50 | delay(1000);
51 |
52 | //方法1:通过广播地址0x00将modbus从机的地址设置为0x10
53 | modbus.writeHoldingRegister(/*id =*/0x00, /*reg =*/DEVICE_ID_REG, /*val =*/0x10);
54 | delay(1000);
55 | uint16_t ret = modbus.readHoldingRegister(0x10, DEVICE_ID_REG);
56 | if(ret == 0x10){
57 | Serial.print("new ID1 is 0x");
58 | Serial.println(ret, HEX);
59 | //方法2:已知从机的地址为0x10,将其修改为0x20
60 | modbus.writeHoldingRegister(/*id =*/0x00, /*reg =*/DEVICE_ID_REG, /*val =*/0x20);
61 | delay(1000);
62 | ret = modbus.readHoldingRegister(0x20, DEVICE_ID_REG);
63 | Serial.print("new ID2 is 0x");
64 | Serial.println(ret, HEX);
65 | }
66 |
67 | }
68 |
69 | void loop() {
70 |
71 | }
72 |
73 |
74 |
--------------------------------------------------------------------------------
/examples/scanModbusID/scanModbusID.ino:
--------------------------------------------------------------------------------
1 | /*!
2 | * @file scanModbusID.ino
3 | * @brief 扫描modbus总线上,所有串口配置为9600波特率,8位数据位,无校验位,1位停止位的modbus从机的地址。
4 | * @n modbus从机设备地址范围为1~247(0x01~0xF7),0为广播地址,所有modbus从机接受到广播包
5 | * @n 都会处理,但不会响应。
6 | * @n 一个modbus主机可以连多个modbus从机,在运行此demo之前,必须知道modbus从机的波特率,数据位,校
7 | * @n 验位,停止位等串口配置。
8 | * @n connected table
9 | * ---------------------------------------------------------------------------------------------------------------
10 | * sensor pin | MCU | Leonardo/Mega2560/M0 | UNO | ESP8266 | ESP32 | microbit |
11 | * VCC | 3.3V/5V | VCC | VCC | VCC | VCC | X |
12 | * GND | GND | GND | GND | GND | GND | X |
13 | * RX | TX | Serial1 RX1 | 5 |5/D6(TX) | D2 | X |
14 | * TX | RX | Serial1 TX1 | 4 |4/D7(RX) | D3 | X |
15 | * ---------------------------------------------------------------------------------------------------------------
16 | * @note: 不支持UNO,Microbit,ESP8266
17 | *
18 | * @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
19 | * @licence The MIT License (MIT)
20 | * @author [Arya](xue.peng@dfrobot.com)
21 | * @version V1.0
22 | * @date 2021-07-16
23 | * @https://github.com/DFRobot/DFRobot_RTU
24 | */
25 | #include "DFRobot_RTU.h"
26 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
27 | #include
28 | #endif
29 |
30 | #define DEVICE_ID_REG 0x02
31 |
32 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
33 | SoftwareSerial mySerial(/*rx =*/4, /*tx =*/5);
34 | DFRobot_RTU modbus(/*s =*/&mySerial);
35 | #else
36 | DFRobot_RTU modbus(/*s =*/&Serial1);
37 | #endif
38 |
39 | void setup() {
40 | Serial.begin(115200);
41 | while(!Serial){ //Waiting for USB Serial COM port to open.
42 | }
43 |
44 | #if defined(ARDUINO_AVR_UNO)||defined(ESP8266)
45 | mySerial.begin(9600);
46 | #elif defined(ESP32)
47 | Serial1.begin(9600, SERIAL_8N1, /*rx =*/3, /*tx =*/1);
48 | #else
49 | Serial1.begin(9600);
50 | #endif
51 | delay(1000);
52 | }
53 |
54 | void loop() {
55 | uint8_t modbusID;
56 | uint16_t ret = 0;
57 | int nDevices;
58 | Serial.println("Scanning...");
59 | nDevices = 0;
60 | for(modbusID = 1; modbusID < 248; modbusID++){
61 | ret = modbus.readHoldingRegister(modbusID, DEVICE_ID_REG);
62 | if(ret == modbusID){
63 | Serial.print("modbus device found at address 0x");
64 | if(modbusID < 16){
65 | Serial.print("0");
66 | }
67 | Serial.print(modbusID,HEX);
68 | Serial.println(" !");
69 | nDevices++;
70 | }
71 | }
72 | if(nDevices == 0)
73 | Serial.println("No modbus devices found\n");
74 | else
75 | Serial.println("done\n");
76 | delay(1000); // wait 1 seconds for next scan
77 | }
78 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Syntax Map For DFRobot_SCT80S16B
3 | #######################################
4 |
5 | #######################################
6 | # Datatypes (KEYWORD1)
7 | #######################################
8 |
9 | DFRobot_RTU KEYWORD1
10 |
11 | #######################################
12 | # Methods and Functions (KEYWORD2)
13 | #######################################
14 |
15 | readCoilsRegisterBit KEYWORD2
16 | readDiscreteInputsRegisterBit KEYWORD2
17 | writeCoilsRegister KEYWORD2
18 | readCoilsRegister KEYWORD2
19 | readDiscreteInputsRegister KEYWORD2
20 | readHoldingRegister KEYWORD2
21 | writeHoldingRegister KEYWORD2
22 |
23 |
24 |
25 | #######################################
26 | # Instances (KEYWORD2)
27 | #######################################
28 |
29 |
30 | #######################################
31 | # Constants (LITERAL1)
32 | #######################################
33 | eRTU_EXCEPTION_ILLEGAL_FUNCTION LITERAL1
34 | eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS LITERAL1
35 | eRTU_EXCEPTION_ILLEGAL_DATA_VALUE LITERAL1
36 | eRTU_EXCEPTION_SLAVE_FAILURE LITERAL1
37 | eRTU_EXCEPTION_CRC_ERROR LITERAL1
38 | eRTU_RECV_ERROR LITERAL1
39 | eRTU_MEMORY_ERROR LITERAL1
40 | eRTU_ID_ERROR LITERAL1
41 | eRtuStatusExceptionCode_t LITERAL1
42 | eCMD_READ_COILS LITERAL1
43 | eCMD_READ_DISCRETE LITERAL1
44 | eCMD_READ_HOLDING LITERAL1
45 | eCMD_WRITE_COILS LITERAL1
46 | eCMD_WRITE_HOLDING LITERAL1
47 | eCMD_WRITE_MULTI_COILS LITERAL1
48 | eCMD_WRITE_MULTI_HOLDING LITERAL1
49 | eFunctionCommand_t LITERAL1
50 | RTU_BROADCAST_ADDRESS LITERAL1
51 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=DFRobot_RTU
2 | version=1.0.3
3 | author=Arya DFRobot
4 | maintainer=DFRobot
5 | sentence=Modbus RTU library for Arduino.
6 | paragraph=A library to use an Arduino as master to control and communicate via modbus protocol.
7 | category=Communication
8 | url=https://github.com/DFRobot/DFRobot_RTU
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/python/raspberrypi/DFRobot_RTU.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | '''
4 | @file DFRobot_RTU.py
5 | @brief Modbus RTU libary for Arduino.
6 |
7 | @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
8 | @licence The MIT License (MIT)
9 | @author [Arya](xue.peng@dfrobot.com)
10 | @version V1.0
11 | @date 2021-07-16
12 | @https://github.com/DFRobot/DFRobot_RTU
13 | '''
14 | import sys
15 | import serial
16 | import time
17 |
18 | class DFRobot_RTU(object):
19 |
20 | _packet_header = {"id": 0, "cmd": 1, "cs": 0}
21 |
22 | '''Enum constant'''
23 | eRTU_EXCEPTION_ILLEGAL_FUNCTION = 0x01
24 | eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS = 0x02
25 | eRTU_EXCEPTION_ILLEGAL_DATA_VALUE = 0x03
26 | eRTU_EXCEPTION_SLAVE_FAILURE = 0x04
27 | eRTU_EXCEPTION_CRC_ERROR = 0x08
28 | eRTU_RECV_ERROR = 0x09
29 | eRTU_MEMORY_ERROR = 0x0A
30 | eRTU_ID_ERROR = 0x0B
31 |
32 | eCMD_READ_COILS = 0x01
33 | eCMD_READ_DISCRETE = 0x02
34 | eCMD_READ_HOLDING = 0x03
35 | eCMD_READ_INPUT = 0x04
36 | eCMD_WRITE_COILS = 0x05
37 | eCMD_WRITE_HOLDING = 0x06
38 | eCMD_WRITE_MULTI_COILS = 0x0F
39 | eCMD_WRITE_MULTI_HOLDING = 0x10
40 |
41 | def __init__(self, baud, bits, parity, stopbit):
42 | '''
43 | @brief Serial initialization.
44 | @param baud: The UART baudrate of raspberry pi
45 | @param bits: The UART data bits of raspberry pi
46 | @param parity: The UART parity bits of raspberry pi
47 | @param stopbit: The UART stopbit bits of raspberry pi.
48 | '''
49 | self._ser = serial.Serial("/dev/ttyAMA0",baud, bits, parity, stopbit)
50 | self._timeout = 0.1 #0.1s
51 |
52 | def set_timout_time_s(self, timeout = 0.1):
53 | '''
54 | @brief Set receive timeout time, unit s.
55 | @param timeout: receive timeout time, unit s, default 0.1s.
56 | '''
57 | self._timeout = timeout
58 |
59 | def read_coils_register(self, id, reg):
60 | '''
61 | @brief Read a coils Register.
62 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
63 | @n but will not answer.
64 | @param reg: Coils register address.
65 | @return Return the value of the coils register value.
66 | @n True: The value of the coils register value is 1.
67 | @n False: The value of the coils register value is 0.
68 | '''
69 | l = [(reg >> 8)&0xFF, (reg & 0xFF), 0x00, 0x01]
70 | val = False
71 | if (id < 1) or (id > 0xF7):
72 | print("device addr error.(1~247) %d"%id)
73 | return 0
74 | l = self._packed(id, self.eCMD_READ_COILS, l)
75 | self._send_package(l)
76 | l = self.recv_and_parse_package(id, self.eCMD_READ_COILS,1)
77 | if (l[0] == 0) and len(l) == 7:
78 | if (l[4] & 0x01) != 0:
79 | val = True
80 | return val
81 |
82 | def read_discrete_inputs_register(self, id, reg):
83 | '''
84 | @brief Read a discrete input register.
85 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
86 | @n but will not answer.
87 | @param reg: Discrete input register address.
88 | @return Return the value of the discrete input register value.
89 | @n True: The value of the discrete input register value is 1.
90 | @n False: The value of the discrete input register value is 0.
91 | '''
92 | l = [(reg >> 8)&0xFF, (reg & 0xFF), 0x00, 0x01]
93 | val = False
94 | if (id < 1) or (id > 0xF7):
95 | print("device addr error.(1~247) %d"%id)
96 | return 0
97 | l = self._packed(id, self.eCMD_READ_DISCRETE, l)
98 | self._send_package(l)
99 | l = self.recv_and_parse_package(id, self.eCMD_READ_DISCRETE,1)
100 | if (l[0] == 0) and len(l) == 7:
101 | if (l[4] & 0x01) != 0:
102 | val = True
103 | return val
104 |
105 | def read_holding_register(self, id, reg):
106 | '''
107 | @brief Read a holding Register.
108 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
109 | @n but will not answer.
110 | @param reg: Holding register address.
111 | @return Return the value of the holding register value.
112 | '''
113 | l = [(reg >> 8)&0xFF, (reg & 0xFF), 0x00, 0x01]
114 | if (id < 1) or (id > 0xF7):
115 | print("device addr error.(1~247) %d"%id)
116 | return 0
117 | l = self._packed(id, self.eCMD_READ_HOLDING, l)
118 | self._send_package(l)
119 | l = self.recv_and_parse_package(id, self.eCMD_READ_HOLDING,2)
120 | if (l[0] == 0) and len(l) == 8:
121 | l[0] = ((l[4] << 8) | l[5]) & 0xFFFF
122 | else:
123 | l[0] = 0
124 | return l[0]
125 |
126 | def read_input_register(self, id, reg):
127 | '''
128 | @brief Read a input Register.
129 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
130 | @n but will not answer.
131 | @param reg: Input register address.
132 | @return Return the value of the holding register value.
133 | '''
134 | l = [(reg >> 8)&0xFF, (reg & 0xFF), 0x00, 0x01]
135 | if (id < 1) or (id > 0xF7):
136 | print("device addr error.(1~247) %d"%id)
137 | return 0
138 | l = self._packed(id, self.eCMD_READ_INPUT, l)
139 | self._send_package(l)
140 | l = self.recv_and_parse_package(id, self.eCMD_READ_INPUT,2)
141 | if (l[0] == 0) and len(l) == 8:
142 | l[0] = ((l[4] << 8) | l[5]) & 0xFFFF
143 | else:
144 | l[0] = 0
145 | return l[0]
146 |
147 | def write_coils_register(self, id, reg, flag):
148 | '''
149 | @brief Write a coils Register.
150 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
151 | @n but will not answer.
152 | @param reg: Coils register address.
153 | @param flag: The value of the register value which will be write, 0 ro 1.
154 | @return Exception code:
155 | @n 0 : sucess.
156 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
157 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
158 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
159 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
160 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
161 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
162 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
163 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
164 | '''
165 | val = 0x0000
166 | re = True
167 | if flag:
168 | val = 0xFF00
169 | re = False
170 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (val >> 8)&0xFF, (val & 0xFF)]
171 | if(id > 0xF7):
172 | print("device addr error.")
173 | return 0
174 | l = self._packed(id, self.eCMD_WRITE_COILS, l)
175 | self._send_package(l)
176 | l = self.recv_and_parse_package(id, self.eCMD_WRITE_COILS,reg)
177 | return l[0]
178 |
179 |
180 | def write_holding_register(self, id, reg, val):
181 | '''
182 | @brief Write a holding register.
183 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
184 | @n but will not answer.
185 | @param reg: Holding register address.
186 | @param val: The value of the register value which will be write.
187 | @return Exception code:
188 | @n 0 : sucess.
189 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
190 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
191 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
192 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
193 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
194 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
195 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
196 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
197 | '''
198 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (val >> 8)&0xFF, (val & 0xFF)]
199 | val = 0
200 | if(id > 0xF7):
201 | print("device addr error.")
202 | return 0
203 | l = self._packed(id, self.eCMD_WRITE_HOLDING, l)
204 | self._send_package(l)
205 | l = self.recv_and_parse_package(id, self.eCMD_WRITE_HOLDING,reg)
206 | return l[0]
207 |
208 | def read_coils_registers(self, id, reg, reg_num):
209 | '''
210 | @brief Read multiple coils Register.
211 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
212 | @n but will not answer.
213 | @param reg: Read the start address of the coil register.
214 | @param reg_num: Number of coils Register.
215 | @return list: format as follow:
216 | @n list[0]: Exception code:
217 | @n 0 : sucess.
218 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
219 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
220 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
221 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
222 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
223 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
224 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
225 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
226 | @n list[1:]: The value of the coil register list.
227 | '''
228 | length = reg_num // 8
229 | mod = reg_num % 8
230 | if mod:
231 | length += 1
232 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (reg_num >> 8) & 0xFF, reg_num & 0xFF]
233 | if (id < 1) or (id > 0xF7):
234 | print("device addr error.(1~247) %d"%id)
235 | return [self.eRTU_ID_ERROR]
236 | l = self._packed(id, self.eCMD_READ_COILS, l)
237 | self._send_package(l)
238 | l = self.recv_and_parse_package(id, self.eCMD_READ_COILS,length)
239 | if ((l[0] == 0) and (len(l) == (5+length+1))):
240 | la = [l[0]] + l[4: len(l)-2]
241 | return la
242 | return [l[0]]
243 |
244 | def read_discrete_inputs_registers(self, id, reg, reg_num):
245 | '''
246 | @brief Read multiple discrete inputs register.
247 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
248 | @n but will not answer.
249 | @param reg: Read the start address of the discrete inputs register.
250 | @param reg_num: Number of coils Register.
251 | @return list: format as follow:
252 | @n list[0]: Exception code:
253 | @n 0 : sucess.
254 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
255 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
256 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
257 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
258 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
259 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
260 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
261 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
262 | @n list[1:]: The value list of the discrete inputs register.
263 | '''
264 | length = reg_num // 8
265 | mod = reg_num % 8
266 | if mod:
267 | length += 1
268 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (reg_num >> 8) & 0xFF, reg_num & 0xFF]
269 | if (id < 1) or (id > 0xF7):
270 | print("device addr error.(1~247) %d"%id)
271 | return [self.eRTU_ID_ERROR]
272 | l = self._packed(id, self.eCMD_READ_DISCRETE, l)
273 | self._send_package(l)
274 | l = self.recv_and_parse_package(id, self.eCMD_READ_DISCRETE,length)
275 | if ((l[0] == 0) and (len(l) == (5+length+1))):
276 | la = [l[0]] + l[4: len(l)-2]
277 | return la
278 | return [l[0]]
279 |
280 | def read_holding_registers(self, id, reg, size):
281 | '''
282 | @brief Read multiple holding register.
283 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
284 | @n but will not answer.
285 | @param reg: Read the start address of the holding register.
286 | @param size: Number of read holding register.
287 | @return list: format as follow:
288 | @n list[0]: Exception code:
289 | @n 0 : sucess.
290 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
291 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
292 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
293 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
294 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
295 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
296 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
297 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
298 | @n list[1:]: The value list of the holding register.
299 | '''
300 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (size >> 8) & 0xFF, size & 0xFF]
301 | if (id < 1) or (id > 0xF7):
302 | print("device addr error.(1~247) %d"%id)
303 | return [self.eRTU_ID_ERROR]
304 | l = self._packed(id, self.eCMD_READ_HOLDING, l)
305 | self._send_package(l)
306 | l = self.recv_and_parse_package(id, self.eCMD_READ_HOLDING,size*2)
307 | #lin = ['%02X' % i for i in l]
308 | #print(" ".join(lin))
309 | if (l[0] == 0) and (len(l) == (5+size*2+1)):
310 | la = [l[0]] + l[4: len(l)-2]
311 | return la
312 | return [l[0]]
313 |
314 | def read_input_registers(self, id, reg, size):
315 | '''
316 | @brief Read multiple input register.
317 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
318 | @n but will not answer.
319 | @param reg: Read the start address of the input register.
320 | @param size: Number of read input register.
321 | @return list: format as follow:
322 | @n list[0]: Exception code:
323 | @n 0 : sucess.
324 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
325 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
326 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
327 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
328 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
329 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
330 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
331 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
332 | @n list[1:]: The value list of the input register.
333 | '''
334 | l = [(reg >> 8)&0xFF, (reg & 0xFF), (size >> 8) & 0xFF, size & 0xFF]
335 | if (id < 1) or (id > 0xF7):
336 | print("device addr error.(1~247) %d"%id)
337 | return [self.eRTU_ID_ERROR]
338 | l = self._packed(id, self.eCMD_READ_INPUT, l)
339 | self._send_package(l)
340 | l = self.recv_and_parse_package(id, self.eCMD_READ_INPUT,size*2)
341 | #lin = ['%02X' % i for i in l]
342 | #print(" ".join(lin))
343 | if (l[0] == 0) and (len(l) == (5+size*2+1)):
344 | la = [l[0]] + l[4: len(l)-2]
345 | return la
346 | return [l[0]]
347 |
348 | def write_coils_registers(self, id, reg, reg_num, data):
349 | '''
350 | @brief Write multiple coils Register.
351 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
352 | @n but will not answer.
353 | @param reg: Write the start address of the coils register.
354 | @param reg_num: Number of coils Register.
355 | @param data: The list of storage coils Registers' value which will be write.
356 | @return Exception code:
357 | @n 0 : sucess.
358 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
359 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
360 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
361 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
362 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
363 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
364 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
365 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
366 | '''
367 | length = reg_num // 8
368 | mod = reg_num % 8
369 | if mod:
370 | length += 1
371 | if len(data) < length:
372 | return [self.eRTU_EXCEPTION_ILLEGAL_DATA_VALUE]
373 | l = [(reg >> 8)&0xFF, (reg & 0xFF), ((reg_num >> 8) & 0xFF), (reg_num & 0xFF), length] + data
374 | if(id > 0xF7):
375 | print("device addr error.")
376 | return 0
377 | l = self._packed(id, self.eCMD_WRITE_MULTI_COILS, l)
378 | self._send_package(l)
379 | l = self.recv_and_parse_package(id, self.eCMD_WRITE_MULTI_COILS,reg)
380 | if (l[0] == 0) and len(l) == 9:
381 | val = ((l[5] << 8) | l[6]) & 0xFFFF
382 | return l[0]
383 |
384 | def write_holding_registers(self, id, reg, data):
385 | '''
386 | @brief Write multiple holding Register.
387 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
388 | @n but will not answer.
389 | @param reg: Write the start address of the holding register.
390 | @param data: The list of storage holding Registers' value which will be write.
391 | @return Exception code:
392 | @n 0 : sucess.
393 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
394 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
395 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
396 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
397 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
398 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
399 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
400 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
401 | '''
402 | size = len(data) >> 1
403 | l = [(reg >> 8)&0xFF, (reg & 0xFF), ((size >> 8) & 0xFF), (size & 0xFF), size*2] + data
404 | if(id > 0xF7):
405 | print("device addr error.")
406 | return 0
407 | l = self._packed(id, self.eCMD_WRITE_MULTI_HOLDING, l)
408 | self._send_package(l)
409 | l = self.recv_and_parse_package(id, self.eCMD_WRITE_MULTI_HOLDING,reg)
410 | if (l[0] == 0) and len(l) == 9:
411 | val = ((l[5] << 8) | l[6]) & 0xFFFF
412 | return l[0]
413 |
414 | def _calculate_crc(self, data):
415 | crc = 0xFFFF
416 | length = len(data)
417 | #print("len=%d"%length)
418 | pos = 0
419 | while pos < length:
420 | crc ^= (data[pos] | 0x0000)
421 | #print("pos=%d, %02x"%(pos,data[pos]))
422 | i = 8;
423 | while i != 0:
424 | if (crc & 0x0001) != 0:
425 | crc = (crc >> 1)&0xFFFF
426 | crc ^= 0xA001
427 | else:
428 | crc = (crc >> 1)&0xFFFF
429 | i -= 1
430 | pos += 1
431 | crc = (((crc & 0x00FF) << 8) | ((crc & 0xFF00) >> 8)) & 0xFFFF
432 | #print("crc=%x"%crc)
433 | return crc
434 |
435 | def _clear_recv_buffer(self):
436 | remain = self._ser.inWaiting()
437 | while remain:
438 | self._ser.read(remain)
439 | remain = self._ser.inWaiting()
440 |
441 | def _packed(self, id, cmd, l):
442 | length = 4+len(l)
443 | #print(len(l))
444 | package = [0]*length
445 | package[0] = id
446 | package[1] = cmd
447 | package[2:length-2] = l
448 | #lin = ['%02X' % i for i in package]
449 | #print(" ".join(lin))
450 |
451 | crc = self._calculate_crc(package[:len(package)-2])
452 | package[length-2] = (crc >> 8) & 0xFF
453 | package[length-1] = crc & 0xFF
454 |
455 | #lin = ['%02X' % i for i in package]
456 | #print(" ".join(lin))
457 | return package;
458 |
459 | def _send_package(self, l):
460 | self._clear_recv_buffer()
461 | if len(l):
462 | self._ser.write(l)
463 | time.sleep(self._timeout)
464 |
465 | def recv_and_parse_package(self, id, cmd, val):
466 | package = [self.eRTU_ID_ERROR]
467 | if id == 0:
468 | return [0]
469 | if (id < 1) or (id > 0xF7):
470 | return package
471 | head = [0]*4
472 | index = 0
473 | t = time.time()
474 | remain = 0
475 | while remain < 4:
476 | if self._ser.inWaiting():
477 | data = self._ser.read(1)
478 | try:
479 | head[index] = ord(data)
480 | except:
481 | head[index] = data
482 | #print("%d = %02X"%(index, head[index]))
483 | index += 1
484 | if (index == 1) and (head[0] != id):
485 | index = 0
486 | elif (index == 2) and ((head[1] & 0x7F) != cmd):
487 | index = 0
488 | remain = index
489 | t = time.time()
490 | if time.time() - t > self._timeout:
491 | #print("time out.")
492 | return [self.eRTU_RECV_ERROR]
493 | if(index == 4):
494 | if head[1] & 0x80:
495 | index = 5
496 | else:
497 | if head[1] < 5:
498 | if head[2] != (val & 0xFF):
499 | index = 0
500 | remain = 0
501 | else:
502 | index = 5 + head[2]
503 | else:
504 | if (((head[2] << 8) | head[3]) & 0xFFFF) != val:
505 | index = 0
506 | remain = 0
507 | else:
508 | index = 8
509 | if index > 0:
510 | package = [0]*(index + 1)
511 | package[1:5] = head
512 | remain = index - 4
513 | index = 5
514 | t = time.time()
515 | while remain > 0:
516 | if self._ser.inWaiting():
517 | data = self._ser.read(1)
518 | try:
519 | package[index] = ord(data)
520 | except:
521 | package[index] = data
522 | index += 1
523 | remain -= 1
524 | t = time.time()
525 | if(time.time() - t >self._timeout):
526 | print("time out1.")
527 | return [self.eRTU_RECV_ERROR]
528 | crc = ((package[len(package) - 2] << 8) | package[len(package) - 1]) & 0xFFFF
529 | if crc != self._calculate_crc(package[1:len(package) - 2]):
530 | print("CRC ERROR")
531 | return [self.eRTU_RECV_ERROR]
532 | if package[2] & 0x80:
533 | package[0] = package[3]
534 | else:
535 | package[0] = 0
536 | #lin = ['%02X' % i for i in package]
537 | #print(" ".join(lin))
538 | return package
539 |
540 |
541 |
542 |
--------------------------------------------------------------------------------
/python/raspberrypi/README.md:
--------------------------------------------------------------------------------
1 | # DFRobot_RTU
2 |
3 | * [中文版](./README_CN.md)
4 |
5 | Modbus RTU libary for raspberrypi. The supported modbus commands are as follows:
6 | * 0x01: Read one or multiple coils register;
7 | * 0x02: Read one or multiple discrete inputs register;
8 | * 0x03: Read one or multiple holding register;
9 | * 0x04: Read one or multiple input register;
10 | * 0x05: Write a coils register;
11 | * 0x06: write a holding register;
12 | * 0x0F: Write multiple coils register;
13 | * 0x10: Write multiple holding register;
14 | * support python2 and python3
15 |
16 | 
17 |
18 |
19 | ## Product Link(链接到英文商城)
20 |
21 |
22 | ## Table of Contents
23 |
24 | * [Summary](#summary)
25 | * [Connected](#connected)
26 | * [Installation](#installation)
27 | * [Calibration](#calibration)
28 | * [Methods](#methods)
29 | * [Compatibility](#compatibility)
30 | * [History](#history)
31 | * [Credits](#credits)
32 |
33 | ## Summary
34 | This is a modbus RTU libary for Arduino by DFRobot.
35 |
36 | ## Connected
37 | Hardware conneted table
38 |
39 | Sensor | raspberrypi
40 | ------------ | :-------------------------------:
41 | VCC | 5V
42 | GND | GND
43 | RX |connected to the UART TX pin of MCU
44 | TX |connected to the UART RX pin of MCU
45 |
46 | ## Installation
47 | To use this library, first download the library file, then open the examples folder and run the demo in the folder Proceed as follows:
48 | * sudo git clone https://github.com/DFRobot/DFRobot_RTU
49 | * cd python
50 | * cd raspberrypi
51 | * cd examples
52 | * python demo_*
53 | * python3 demo_*
54 |
55 |
56 | ## Methods
57 |
58 | ```C++
59 | '''
60 | @brief Serial initialization.
61 | @param baud: The UART baudrate of raspberry pi
62 | @param bits: The UART data bits of raspberry pi
63 | @param parity: The UART parity bits of raspberry pi
64 | @param stopbit: The UART stopbit bits of raspberry pi.
65 | '''
66 | def __init__(self, baud, bits, parity, stopbit):
67 |
68 | '''
69 | @brief Set receive timeout time, unit s.
70 | @param timeout: receive timeout time, unit s, default 0.1s.
71 | '''
72 | def set_timout_time_s(self, timeout):
73 |
74 | '''
75 | @brief Read a coils Register.
76 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
77 | @n but will not answer.
78 | @param reg: Coils register address.
79 | @return Return the value of the coils register value.
80 | @n True: The value of the coils register value is 1.
81 | @n False: The value of the coils register value is 0.
82 | '''
83 | def read_coils_register(self, id, reg):
84 |
85 | '''
86 | @brief Read a discrete input register.
87 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
88 | @n but will not answer.
89 | @param reg: Discrete input register address.
90 | @return Return the value of the discrete input register value.
91 | @n True: The value of the discrete input register value is 1.
92 | @n False: The value of the discrete input register value is 0.
93 | '''
94 | def read_discrete_inputs_register(self, id, reg):
95 |
96 | '''
97 | @brief Read a holding Register.
98 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
99 | @n but will not answer.
100 | @param reg: Holding register address.
101 | @return Return the value of the holding register value.
102 | '''
103 | def read_holding_register(self, id, reg):
104 |
105 | '''
106 | @brief Read a input Register.
107 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
108 | @n but will not answer.
109 | @param reg: Input register address.
110 | @return Return the value of the holding register value.
111 | '''
112 | def read_input_register(self, id, reg):
113 |
114 | '''
115 | @brief Write a coils Register.
116 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
117 | @n but will not answer.
118 | @param reg: Coils register address.
119 | @param flag: The value of the register value which will be write, True ro False.
120 | @return Exception code:
121 | @n 0 : sucess.
122 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
123 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
124 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
125 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
126 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
127 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
128 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
129 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
130 | '''
131 | def write_coils_register(self, id, reg, flag):
132 |
133 | '''
134 | @brief Write a holding register.
135 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
136 | @n but will not answer.
137 | @param reg: Holding register address.
138 | @param val: The value of the register value which will be write.
139 | @return Exception code:
140 | @n 0 : sucess.
141 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
142 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
143 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
144 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
145 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
146 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
147 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
148 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
149 | '''
150 | def write_holding_register(self, id, reg, val):
151 |
152 | '''
153 | @brief Read multiple coils Register.
154 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
155 | @n but will not answer.
156 | @param reg: Read the start address of the coil register.
157 | @param reg_num: Number of coils Register.
158 | @return list: format as follow:
159 | @n list[0]: Exception code:
160 | @n 0 : sucess.
161 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
162 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
163 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
164 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
165 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
166 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
167 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
168 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
169 | @n list[1:]: The value of the coil register list.
170 | '''
171 | def read_coils_registers(self, id, reg, reg_num):
172 |
173 | '''
174 | @brief Read multiple discrete inputs register.
175 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
176 | @n but will not answer.
177 | @param reg: Read the start address of the discrete inputs register.
178 | @param reg_num: Number of coils Register.
179 | @return list: format as follow:
180 | @n list[0]: Exception code:
181 | @n 0 : sucess.
182 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
183 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
184 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
185 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
186 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
187 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
188 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
189 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
190 | @n list[1:]: The value list of the discrete inputs register.
191 | '''
192 | def read_discrete_inputs_registers(self, id, reg, reg_num):
193 |
194 | '''
195 | @brief Read multiple holding register.
196 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
197 | @n but will not answer.
198 | @param reg: Read the start address of the holding register.
199 | @param len: Number of read holding register.
200 | @return list: format as follow:
201 | @n list[0]: Exception code:
202 | @n 0 : sucess.
203 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
204 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
205 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
206 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
207 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
208 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
209 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
210 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
211 | @n list[1:]: The value list of the holding register.
212 | '''
213 | def read_holding_registers(self, id, reg, size):
214 |
215 | '''
216 | @brief Read multiple input register.
217 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
218 | @n but will not answer.
219 | @param reg: Read the start address of the input register.
220 | @param size: Number of read input register.
221 | @return list: format as follow:
222 | @n list[0]: Exception code:
223 | @n 0 : sucess.
224 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
225 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
226 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
227 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
228 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
229 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
230 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
231 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
232 | @n list[1:]: The value list of the input register.
233 | '''
234 | def read_input_registers(self, id, reg, size):
235 |
236 | '''
237 | @brief Write multiple coils Register.
238 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
239 | @n but will not answer.
240 | @param reg: Write the start address of the coils register.
241 | @param reg_num: Number of coils Register.
242 | @param data: The list of storage coils Registers' value which will be write.
243 | @return Exception code:
244 | @n 0 : sucess.
245 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
246 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
247 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
248 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
249 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
250 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
251 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
252 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
253 | '''
254 | def write_coils_registers(self, id, reg, reg_num, data):
255 |
256 | '''
257 | @brief Write multiple holding Register.
258 | @param id: modbus device ID. Range: 0x00 ~ 0xF7(0~247), 0x00 is broadcasr address, which all slaves will process broadcast packets,
259 | @n but will not answer.
260 | @param reg: Write the start address of the holding register.
261 | @param data: The list of storage holding Registers' value which will be write.
262 | @return Exception code:
263 | @n 0 : sucess.
264 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : Illegal function.
265 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: Illegal data address.
266 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: Illegal data value.
267 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: Slave failure.
268 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC check error.
269 | @n 9 or eRTU_RECV_ERROR: Receive packet error.
270 | @n 10 or eRTU_MEMORY_ERROR: Memory error.
271 | @n 11 or eRTU_ID_ERROR: Broadcasr address or error ID
272 | '''
273 | def write_holding_registers(self, id, reg, data):
274 |
275 | ```
276 |
277 | ## Compatibility
278 |
279 | MCU | SoftwareSerial | HardwareSerial | IO
280 | ------------------ | :----------: | :----------: | :---------:
281 | Arduino Uno | √ | X | √
282 | Mega2560 | √ | √ | √
283 | Leonardo | √ | √ | √
284 | ESP32 | X | √ | √
285 | ESP8266 | √ | X | √
286 | micro:bit | X | X | √
287 | FireBeetle M0 | X | √ | √
288 | raspberry | X | √ | √
289 |
290 | ## History
291 |
292 | - Data 2021-07-18
293 | - Version V1.0
294 |
295 | ## Credits
296 |
297 | Written by(xue.peng@dfrobot.com), 2021. (Welcome to our [website](https://www.dfrobot.com/))
298 |
299 |
300 |
301 |
302 |
303 |
--------------------------------------------------------------------------------
/python/raspberrypi/README_CN.md:
--------------------------------------------------------------------------------
1 | # DFRobot_RTU
2 |
3 | * [English Version](./README.md)
4 |
5 | 这是一个基于Modbus RTU协议的raspberry pi python modbus库,它支持以下几种modbus协议命令:
6 | * 0x01: 读一个或多个线圈寄存器;
7 | * 0x02: 读一个或多个离散输入寄存器;
8 | * 0x03: 读一个或多个保持寄存器;
9 | * 0x04: 读一个或多个输入寄存器;
10 | * 0x05: 写单个线圈寄存器;
11 | * 0x06: 写单个保持寄存器;
12 | * 0x0F: 写多个线圈寄存器;
13 | * 0x10: 写多个保持寄存器;
14 |
15 | 
16 |
17 |
18 | ## Product Link(链接到英文商城)
19 |
20 |
21 | ## Table of Contents
22 |
23 | * [Summary](#summary)
24 | * [Connected](#connected)
25 | * [Installation](#installation)
26 | * [Calibration](#calibration)
27 | * [Methods](#methods)
28 | * [Compatibility](#compatibility)
29 | * [History](#history)
30 | * [Credits](#credits)
31 |
32 | ## Summary
33 | 这是DFRobot基于modbus RTU协议为raspberry pi 平台移植的python modbus库。
34 |
35 |
36 | ## Connected
37 | Hardware conneted table
38 |
39 | Sensor | raspberry pi |
40 | ------------ | :-------------------------------: |
41 | VCC | 5V |
42 | GND | GND |
43 | RX |connected to the UART TX pin of MCU|
44 | TX |connected to the UART RX pin of MCU|
45 |
46 | ## Installation
47 | To use this library, first download the library file, then open the examples folder and run the demo in the folder Proceed as follows:
48 | * sudo git clone https://github.com/DFRobot/DFRobot_RTU
49 | * cd python
50 | * cd raspberrypi
51 | * cd examples
52 | * python demo_*
53 | * python3 demo_*
54 |
55 | ## Methods
56 |
57 | ```C++
58 | '''
59 | @brief 树莓派串口通信配置.
60 | @param baud: 树莓派串口通信波特率参数
61 | @param bits: 树莓派串口通信数据位参数
62 | @param parity: 树莓派串口通信校验位参数
63 | @param stopbit: 树莓派串口通信停止位参数
64 | '''
65 | def __init__(self, baud, bits, parity, stopbit):
66 |
67 | '''
68 | @brief 设置接收超时时间,单位s.
69 | @param timeout: 接收超时形参,单位秒,默认为0.1s
70 | '''
71 | def set_timout_time_s(self, timeout = 0.1):
72 |
73 | '''
74 | @brief 读取线圈寄存器的值。
75 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
76 | @param reg: 线圈寄存器地址。
77 | @return 返回线圈寄存器的值。
78 | @n True: 线圈寄存器的值为1
79 | @n False: 线圈寄存器的值为0
80 | '''
81 | def read_coils_register(self, id, reg):
82 |
83 | '''
84 | @brief 读取离散输入寄存器的值。
85 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
86 | @param reg: 离散输入寄存器地址。
87 | @return 返回离散输入寄存器的值。
88 | @n True: 离散输入寄存器的值为1
89 | @n False: 离散输入寄存器的值为0
90 | '''
91 | def read_discrete_inputs_register(self, id, reg):
92 |
93 | '''
94 | @brief 读取保持寄存器的值。
95 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
96 | @param reg: 保持寄存器地址。
97 | @return 返回保持寄存器的值。
98 | '''
99 | def read_holding_register(self, id, reg):
100 |
101 | '''
102 | @brief 读取输入寄存器的值。
103 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
104 | @param reg: 输入寄存器地址。
105 | @return 返回输入寄存器的值。
106 | '''
107 | def read_input_register(self, id, reg):
108 |
109 | '''
110 | @brief 写单个线圈寄存器的值。
111 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
112 | @param reg: 线圈寄存器地址。
113 | @param flag: 将要被写入的线圈寄存器的值,True or False
114 | @return 返回写入的线圈寄存器的值
115 | '''
116 | def write_coils_register(self, id, reg, flag):
117 |
118 | '''
119 | @brief 写单个保持寄存器的值。
120 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
121 | @param reg: 保持寄存器地址。
122 | @param val: 将要被写入的保持寄存器的值
123 | @return 返回写入的保持寄存器的值
124 | '''
125 | def write_holding_register(self, id, reg, val):
126 |
127 | '''
128 | @brief 读取多个线圈寄存器的值。
129 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
130 | @param reg: 读取线圈寄存器的起始地址。
131 | @param reg_num: 离散输入寄存器的个数
132 | @return 列表: 格式如下:
133 | @n list[0]: Exception code:
134 | @n 0 : sucess.
135 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
136 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
137 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
138 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
139 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
140 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
141 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
142 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
143 | @n list[1:]: 读取的线圈寄存器的值.
144 | '''
145 | def read_coils_registers(self, id, reg, reg_num):
146 |
147 | '''
148 | @brief 读取多个离散输入寄存器的值。
149 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
150 | @param reg: 读取离散输入寄存器的起始地址。
151 | @param reg_num: 离散输入寄存器的个数
152 | @return 列表: 格式如下:
153 | @n list[0]: Exception code:
154 | @n 0 : sucess.
155 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
156 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
157 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
158 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
159 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
160 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
161 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
162 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
163 | @n list[1:]: 读取的离散输入寄存器的值.
164 | '''
165 | def read_discrete_inputs_registers(self, id, reg, reg_num):
166 |
167 | '''
168 | @brief 读取多个保持寄存器的值。
169 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
170 | @param reg: 读取保持寄存器的起始地址。
171 | @param len: 读取保持寄存器的个数
172 | @return 列表: 格式如下:
173 | @n list[0]: Exception code:
174 | @n 0 : sucess.
175 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
176 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
177 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
178 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
179 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
180 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
181 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
182 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
183 | @n list[1:]: 读取的保持寄存器的值.
184 | '''
185 | def read_holding_registers(self, id, reg, size):
186 |
187 | '''
188 | @brief 读取多个输入寄存器的值。
189 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
190 | @param reg: 读取输入寄存器的起始地址。
191 | @param len: 读取输入寄存器的个数
192 | @return 列表: 格式如下:
193 | @n list[0]: Exception code:
194 | @n 0 : sucess.
195 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
196 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
197 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
198 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
199 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
200 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
201 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
202 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
203 | @n list[1:]: 读取的输入寄存器的值.
204 | '''
205 | def read_input_registers(self, id, reg, size):
206 |
207 | '''
208 | @brief 写多个线圈寄存器的值。
209 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
210 | @param reg: 写线圈寄存器的起始地址。
211 | @param reg_num: 线圈寄存器的个数.
212 | @param data: 将要被写入的线圈寄存器的列表
213 | @return Exception code:
214 | @n 0 : sucess.
215 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
216 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
217 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
218 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
219 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
220 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
221 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
222 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
223 | '''
224 | def write_coils_registers(self, id, reg, reg_num, data):
225 |
226 | '''
227 | @brief 写多个保持寄存器的值。
228 | @param id: modbus 设备ID,范围0~0xF7(0~247),其中0x00为广播地址,所有modbus从机都会处理广播包,但不会应答。
229 | @param reg: 写保持寄存器的起始地址。
230 | @param data: 将要被写入的保持寄存器的列表
231 | @return Exception code:
232 | @n 0 : sucess.
233 | @n 1 or eRTU_EXCEPTION_ILLEGAL_FUNCTION : 非法功能.
234 | @n 2 or eRTU_EXCEPTION_ILLEGAL_DATA_ADDRESS: 非法数据地址.
235 | @n 3 or eRTU_EXCEPTION_ILLEGAL_DATA_VALUE: 非法数据值.
236 | @n 4 or eRTU_EXCEPTION_SLAVE_FAILURE: 从机故障.
237 | @n 8 or eRTU_EXCEPTION_CRC_ERROR: CRC校验错误.
238 | @n 9 or eRTU_RECV_ERROR: 接收包错误.
239 | @n 10 or eRTU_MEMORY_ERROR: 内存错误.
240 | @n 11 or eRTU_ID_ERROR:广播地址或错误ID(因为主机无法收到从机广播包的应答)
241 | '''
242 | def write_holding_registers(self, id, reg, data):
243 | ```
244 |
245 | ## Compatibility
246 |
247 | MCU | SoftwareSerial | HardwareSerial |
248 | ------------------ | :----------: | :----------: |
249 | Arduino Uno | √ | X |
250 | Mega2560 | √ | √ |
251 | Leonardo | √ | √ |
252 | ESP32 | X | √ |
253 | ESP8266 | √ | X |
254 | micro:bit | X | X |
255 | FireBeetle M0 | X | √ |
256 | raspberry | X | √ |
257 |
258 | ## History
259 |
260 | - Data 2021-07-17
261 | - Version V1.0
262 |
263 | ## Credits
264 |
265 | Written by(xue.peng@dfrobot.com), 2021. (Welcome to our [website](https://www.dfrobot.com/))
266 |
--------------------------------------------------------------------------------
/python/raspberrypi/examples/modify_modbus_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | '''
4 | # modify_modbus_id.py
5 | #
6 | # @brief 修改modbus从机的设备ID。每个modbus从机都有唯一识别的设备ID号,范围0x0000~0x00F7(0~247),修改设备ID有2种方式:
7 | # @n 1: 不知道设备的ID地址,可以通过广播地址0x00修改从机的ID地址,此命令会将总线上所有的从机的地址都修改为设置的ID(用0x00修改地址时,总线上最好只接一个设备)
8 | # @n 2: 知道设备的ID地址,直接用ID修改
9 | # @n note:运行此demo必须知道设备的串口配置(波特率,数据位,校验位,停止位)
10 | #
11 | # @n connected
12 | # -----------------------------------------------------------------------------
13 | # sensor pin | MCU | raspberry pi |
14 | # VCC | 3.3V/5V | 5V/3V3 |
15 | # GND | GND | GND |
16 | # RX | TX | (BCM)14 TX |
17 | # TX | RX | (BCM)15 RX |
18 | # -----------------------------------------------------------------------------
19 | #
20 | # @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
21 | # @licence The MIT License (MIT)
22 | # @author [Arya](xue.peng@dfrobot.com)
23 | # @version V1.0
24 | # @date 2021-07-16
25 | # @https://github.com/DFRobot/DFRobot_RTU
26 | '''
27 |
28 | import sys
29 | import os
30 | import time
31 |
32 | sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
33 | from DFRobot_RTU import *
34 |
35 | modbus = DFRobot_RTU(9600, 8, 'N', 1)
36 |
37 | if __name__ == "__main__":
38 | DEVICE_ID_REG = 0x02
39 | '''方法1:通过广播地址0x00将modbus从机的地址设置为0x10'''
40 | modbus.write_holding_register(id = 0x00, reg = DEVICE_ID_REG, val = 0x10)
41 | time.sleep(1)
42 | ret = modbus.read_holding_register(id = 0x10, reg = DEVICE_ID_REG)
43 | if ret == 0x10:
44 | print("new ID1 is %02X"%ret)
45 | '''方法2:已知从机的地址为0x10,将其修改为0x20'''
46 | modbus.write_holding_register(id = 0x10, reg = DEVICE_ID_REG, val = 0x20)
47 | time.sleep(1)
48 | ret = modbus.read_holding_register(id = 0x20, reg = DEVICE_ID_REG)
49 | print("new ID2 is %02X"%ret)
50 |
51 |
--------------------------------------------------------------------------------
/python/raspberrypi/examples/scan_modbus_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | '''
4 | # scan_modbus_id.py
5 | #
6 | # @brief 扫描modbus总线上,所有串口配置为9600波特率,8位数据位,无校验位,1位停止位的modbus从机的地址。
7 | # @n modbus从机设备地址范围为1~247(0x01~0xF7),0为广播地址,所有modbus从机接受到广播包都会处理,但不会响应。
8 | # @n 一个modbus主机可以连多个modbus从机,在运行此demo之前,必须知道modbus从机的波特率,数据位,校验位,停止位等串口配置。
9 | #
10 | # @n connected
11 | # -----------------------------------------------------------------------------
12 | # sensor pin | MCU | raspberry pi |
13 | # VCC | 3.3V/5V | 5V/3V3 |
14 | # GND | GND | GND |
15 | # RX | TX | (BCM)14 TX |
16 | # TX | RX | (BCM)15 RX |
17 | # -----------------------------------------------------------------------------
18 | #
19 | # @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
20 | # @licence The MIT License (MIT)
21 | # @author [Arya](xue.peng@dfrobot.com)
22 | # @version V1.0
23 | # @date 2021-07-16
24 | # @https://github.com/DFRobot/DFRobot_RTU
25 | '''
26 |
27 | import sys
28 | import os
29 | import time
30 |
31 | sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
32 | from DFRobot_RTU import *
33 |
34 | modbus = DFRobot_RTU(9600, 8, 'N', 1)
35 |
36 | if __name__ == "__main__":
37 | DEVICE_ID_REG = 0x02
38 | while True:
39 | modbus_id = 1
40 | n_devices = 0
41 | print("Scanning...")
42 | while modbus_id < 248:
43 | ret = modbus.read_holding_register(modbus_id, DEVICE_ID_REG)
44 | if ret == modbus_id:
45 | print("modbus device found at address 0x%02X !"%modbus_id)
46 | n_devices += 1
47 | modbus_id += 1
48 | if n_devices == 0:
49 | print("No modbus devices found\n")
50 | else:
51 | print("done\n")
52 | time.sleep(1)
53 |
--------------------------------------------------------------------------------
/python/raspberrypi/examples/test.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | '''
4 | # test.py
5 | #
6 | # @brief 测试发送命令
7 | #
8 | # @n connected
9 | # -----------------------------------------------------------------------------
10 | # sensor pin | MCU | raspberry pi |
11 | # VCC | 3.3V/5V | 5V/3V3 |
12 | # GND | GND | GND |
13 | # RX | TX | (BCM)14 TX |
14 | # TX | RX | (BCM)15 RX |
15 | # -----------------------------------------------------------------------------
16 | #
17 | # @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
18 | # @licence The MIT License (MIT)
19 | # @author [Arya](xue.peng@dfrobot.com)
20 | # @version V1.0
21 | # @date 2021-07-16
22 | # @https://github.com/DFRobot/DFRobot_RTU
23 | '''
24 |
25 | import sys
26 | import os
27 | import time
28 |
29 | sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
30 | from DFRobot_RTU import *
31 |
32 | modbus = DFRobot_RTU(9600, 8, 'N', 1)
33 |
34 | if __name__ == "__main__":
35 | DEVICE_ID_REG = 0x02
36 | while True:
37 | print("read_coils_register:")
38 | modbus.read_coils_register(id = 0x20, reg = 0x00)
39 | print("read_discrete_inputs_register:")
40 | modbus.read_discrete_inputs_register(id = 0x20, reg = 0x00)
41 | print("read_discrete_inputs_register:")
42 | modbus.read_discrete_inputs_register(id = 0x20, reg = 0x00)
43 | print("read_coils_registers:")
44 | modbus.read_coils_registers(id = 0x20, reg = 0x00, reg_num = 1)
45 | print("read_discrete_inputs_registers:")
46 | modbus.read_discrete_inputs_registers(id = 0x20, reg = 0x00, reg_num = 1)
47 | print("read_holding_registers:")
48 | modbus.read_holding_registers(id = 0x20, reg = 0x00, size = 1)
49 | print("write_coils_registers:")
50 | modbus.write_coils_registers(id = 0x20, reg = 0x02, reg_num = 1, data = [0xFF])
51 | print("write_holding_registers:")
52 | modbus.write_holding_registers(id = 0x20, reg = 0x02, data = [0x00,0x20])
53 | print("write_coils_register:")
54 | modbus.write_coils_register(id = 0x20, reg = 0x02, flag = True)
55 | print("write_holding_register:")
56 | modbus.write_holding_register(id = 0x20, reg = 0x02, val = 0x20)
57 |
58 | print("\n")
59 | time.sleep(2)
60 |
--------------------------------------------------------------------------------
/src/DFRobot_RTU.cpp:
--------------------------------------------------------------------------------
1 | /*!
2 | * @file DFRobot_RTU.cpp
3 | * @brief Modbus RTU libary for Arduino.
4 | *
5 | * @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
6 | * @licence The MIT License (MIT)
7 | * @author [Arya](xue.peng@dfrobot.com)
8 | * @version V1.0
9 | * @date 2021-07-16
10 | * @https://github.com/DFRobot/DFRobot_RTU
11 | */
12 | #include
13 | #include "DFRobot_RTU.h"
14 |
15 | DFRobot_RTU::DFRobot_RTU(Stream *s,int dePin)
16 | :_timeout(100), _s(s),_dePin(dePin){
17 | if(_dePin>0){
18 | pinMode(_dePin,OUTPUT);
19 | }
20 | }
21 |
22 | DFRobot_RTU::DFRobot_RTU(Stream *s)
23 | :_timeout(100), _s(s),_dePin(-1){
24 | if(_dePin>0){
25 | pinMode(_dePin,OUTPUT);
26 | }
27 | }
28 |
29 | DFRobot_RTU::DFRobot_RTU()
30 | : _timeout(100), _s(NULL),_dePin(-1){
31 | if(_dePin>0){
32 | pinMode(_dePin,OUTPUT);
33 | }
34 | }
35 |
36 | void DFRobot_RTU::setTimeoutTimeMs(uint32_t timeout){
37 | _timeout = timeout;
38 | }
39 |
40 | bool DFRobot_RTU::readCoilsRegister(uint8_t id, uint16_t reg){
41 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), 0x00, 0x01};
42 | bool val = false;
43 | uint8_t *pData = NULL;
44 | uint8_t ret = 0;
45 | if((id == 0) && (id > 0xF7)){
46 | RTU_DBG("Device id error");
47 | return 0;
48 | }
49 | pRtuPacketHeader_t header = packed(id, eCMD_READ_COILS, temp, sizeof(temp));
50 | sendPackage(header);
51 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_COILS, 1, &ret);
52 | if((ret == 0) && (header != NULL)){
53 | pData = header->payload;
54 | if (pData[1] & 0x01) val = true;
55 | }
56 | RTU_DBG(val, HEX);
57 | return val;
58 | }
59 |
60 | bool DFRobot_RTU::readDiscreteInputsRegister(uint8_t id, uint16_t reg){
61 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), 0x00, 0x01};
62 | uint8_t *pData = NULL;
63 | bool val = false;
64 | uint8_t ret = 0;
65 | if((id == 0) && (id > 0xF7)){
66 | RTU_DBG("Device id error");
67 | return 0;
68 | }
69 | pRtuPacketHeader_t header = packed(id, eCMD_READ_DISCRETE, temp, sizeof(temp));
70 | sendPackage(header);
71 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_DISCRETE, 1, &ret);
72 | if((ret == 0) && (header != NULL)){
73 | pData = header->payload;
74 | if (pData[1] & 0x01) val = true;
75 | free(header);
76 | }
77 | RTU_DBG(val, HEX);
78 | return val;
79 | }
80 |
81 | uint16_t DFRobot_RTU::readHoldingRegister(uint8_t id, uint16_t reg){
82 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), 0x00, 0x01};
83 | uint16_t val = 0;
84 | uint8_t *pData = NULL;
85 | uint8_t ret = 0;
86 | if((id == 0) && (id > 0xF7)){
87 | RTU_DBG("Device id error");
88 | return 0;
89 | }
90 | pRtuPacketHeader_t header = packed(id, eCMD_READ_HOLDING, temp, sizeof(temp));
91 | sendPackage(header);
92 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_HOLDING, 2, &ret);
93 | if((ret == 0) && (header != NULL)){
94 | pData = header->payload;
95 | val = (pData[1] << 8) | pData[2];
96 | free(header);
97 | }
98 | //RTU_DBG(val, HEX);
99 | return val;
100 | }
101 |
102 | uint16_t DFRobot_RTU::readInputRegister(uint8_t id, uint16_t reg){
103 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), 0x00, 0x01};
104 | uint16_t val = 0;
105 | uint8_t *pData = NULL;
106 | uint8_t ret = 0;
107 | if((id == 0) && (id > 0xF7)){
108 | RTU_DBG("Device id error");
109 | return 0;
110 | }
111 | pRtuPacketHeader_t header = packed(id, eCMD_READ_INPUT, temp, sizeof(temp));
112 | RTU_DBG(header->len,HEX);
113 | RTU_DBG(header->id,HEX);
114 | RTU_DBG(header->cmd,HEX);
115 | for(uint8_t i=0;ipayload)[i],HEX);
117 | sendPackage(header);
118 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_INPUT, 2, &ret);
119 | if((ret == 0) && (header != NULL)){
120 | pData = header->payload;
121 | val = (pData[1] << 8) | pData[2];
122 | free(header);
123 | }
124 | RTU_DBG(val, HEX);
125 | return val;
126 | }
127 |
128 | uint8_t DFRobot_RTU::writeCoilsRegister(uint8_t id, uint16_t reg, bool flag){
129 | uint16_t val = flag ? 0xFF00 : 0x0000;
130 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((val >> 8) & 0xFF), (uint8_t)(val & 0xFF)};
131 | uint8_t ret = 0;
132 | if(id > 0xF7){
133 | RTU_DBG("Device id error");
134 | return 0;
135 | }
136 | pRtuPacketHeader_t header = packed(id, eCMD_WRITE_COILS, temp, sizeof(temp));
137 | sendPackage(header);
138 | header = recvAndParsePackage(id, (uint8_t)eCMD_WRITE_COILS, reg, &ret);
139 | if((ret == 0) && (header != NULL)){
140 | free(header);
141 | }
142 | return ret;
143 | }
144 | uint8_t DFRobot_RTU::writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t val){
145 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((val >> 8) & 0xFF), (uint8_t)(val & 0xFF)};
146 | uint8_t ret = 0;
147 | if(id > 0xF7){
148 | RTU_DBG("Device id error");
149 | return 0;
150 | }
151 | pRtuPacketHeader_t header = packed(id, eCMD_WRITE_HOLDING, temp, sizeof(temp));
152 | //uint8_t *data = (uint8_t *)header;
153 | //for (int i = 0; i < sizeof(sRtuPacketHeader_t); i++){
154 | // if (data != NULL) RTU_DBG(*data, HEX);
155 | // data++;
156 | //}
157 |
158 | sendPackage(header);
159 | header = recvAndParsePackage(id, (uint8_t)eCMD_WRITE_HOLDING, reg, &ret);
160 | val = 0xFFFF;
161 | if((ret == 0) && (header != NULL)){
162 | val = (((uint8_t *)header->payload)[2] << 8) | ((uint8_t *)header->payload)[3];
163 | free(header);
164 | }
165 | //RTU_DBG(val, HEX);
166 | return ret;
167 | }
168 |
169 | uint8_t DFRobot_RTU::readCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size){
170 | uint8_t length = regNum/8 + ((regNum%8) ? 1 : 0);
171 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((regNum >> 8) & 0xFF), (uint8_t)(regNum & 0xFF)};
172 | uint8_t ret = 0;
173 | if((id == 0) && (id > 0xF7)){
174 | RTU_DBG("Device id error");
175 | return eRTU_ID_ERROR;
176 | }
177 | pRtuPacketHeader_t header = packed(id, eCMD_READ_COILS, temp, sizeof(temp));
178 | sendPackage(header);
179 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_COILS, length, &ret);
180 | if((ret == 0) && (header != NULL)){
181 | if(data != NULL){
182 | size = (size > length) ? length : size;
183 | memcpy(data, (uint8_t *)&(header->payload[1]), size);
184 | }
185 | free(header);
186 | }
187 | return ret;
188 | }
189 | uint8_t DFRobot_RTU::readDiscreteInputsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size){
190 | uint8_t length = regNum/8 + ((regNum%8) ? 1 : 0);
191 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((regNum >> 8) & 0xFF), (uint8_t)(regNum & 0xFF)};
192 | uint8_t ret = 0;
193 | if((id == 0) && (id > 0xF7)){
194 | RTU_DBG("Device id error");
195 | return eRTU_ID_ERROR;
196 | }
197 | pRtuPacketHeader_t header = packed(id, eCMD_READ_DISCRETE, temp, sizeof(temp));
198 | sendPackage(header);
199 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_DISCRETE, length, &ret);
200 | if((ret == 0) && (header != NULL)){
201 | if(data != NULL){
202 | size = (size > length) ? length : size;
203 | memcpy(data, (uint8_t *)&(header->payload[1]), size);
204 | }
205 | free(header);
206 | }
207 | return ret;
208 | }
209 | uint8_t DFRobot_RTU::readHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size){
210 | uint8_t length = size/2 + ((size%2) ? 1 : 0);
211 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((length >> 8) & 0xFF), (uint8_t)(length & 0xFF)};
212 | uint8_t ret = 0;
213 | if((id == 0) && (id > 0xF7)){
214 | RTU_DBG("Device id error");
215 | return eRTU_ID_ERROR;
216 | }
217 | pRtuPacketHeader_t header = packed(id, eCMD_READ_HOLDING, temp, sizeof(temp));
218 | sendPackage(header);
219 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_HOLDING, length*2, &ret);
220 | if((ret == 0) && (header != NULL)){
221 | if(data != NULL) memcpy(data, (uint8_t *)&(header->payload[1]), size);
222 | free(header);
223 | }
224 | return ret;
225 | }
226 |
227 | uint8_t DFRobot_RTU::readInputRegister(uint8_t id, uint16_t reg, void *data, uint16_t size){
228 | uint8_t length = size/2 + ((size%2) ? 1 : 0);
229 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((length >> 8) & 0xFF), (uint8_t)(length & 0xFF)};
230 | uint8_t ret = 0;
231 | if((id == 0) && (id > 0xF7)){
232 | RTU_DBG("Device id error");
233 | return eRTU_ID_ERROR;
234 | }
235 | pRtuPacketHeader_t header = packed(id, eCMD_READ_INPUT, temp, sizeof(temp));
236 | sendPackage(header);
237 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_INPUT, length*2, &ret);
238 | if((ret == 0) && (header != NULL)){
239 | if(data != NULL) memcpy(data, (uint8_t *)&(header->payload[1]), size);
240 | free(header);
241 | }
242 | RTU_DBG(val, HEX);
243 | return ret;
244 | }
245 |
246 | uint8_t DFRobot_RTU::readHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum){
247 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((regNum >> 8) & 0xFF), (uint8_t)(regNum & 0xFF)};
248 | uint8_t ret = 0;
249 | if((id == 0) && (id > 0xF7)){
250 | RTU_DBG("Device id error");
251 | return eRTU_ID_ERROR;
252 | }
253 | pRtuPacketHeader_t header = packed(id, eCMD_READ_HOLDING, temp, sizeof(temp));
254 | sendPackage(header);
255 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_HOLDING, regNum*2, &ret);
256 | if((ret == 0) && (header != NULL)){
257 | if(data != NULL){
258 | for(int i = 0; i < regNum; i++){
259 | data[i] = ((((uint8_t *)header->payload)[1+2*i]) << 8) | (((uint8_t *)header->payload)[2+2*i]);
260 | }
261 | }
262 | free(header);
263 | }
264 | return ret;
265 | }
266 |
267 | uint8_t DFRobot_RTU::readInputRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum){
268 | uint8_t temp[] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)((regNum >> 8) & 0xFF), (uint8_t)(regNum & 0xFF)};
269 | uint8_t ret = 0;
270 | if((id == 0) && (id > 0xF7)){
271 | RTU_DBG("Device id error");
272 | return eRTU_ID_ERROR;
273 | }
274 | pRtuPacketHeader_t header = packed(id, eCMD_READ_INPUT, temp, sizeof(temp));
275 | sendPackage(header);
276 | header = recvAndParsePackage(id, (uint8_t)eCMD_READ_INPUT, regNum*2, &ret);
277 | if((ret == 0) && (header != NULL)){
278 | if(data != NULL){
279 | for(int i = 0; i < regNum; i++){
280 | data[i] = ((((uint8_t *)header->payload)[1+2*i]) << 8) | (((uint8_t *)header->payload)[2+2*i]);
281 | }
282 | }
283 | free(header);
284 | }
285 | return ret;
286 | }
287 |
288 | uint8_t DFRobot_RTU::writeCoilsRegister(uint8_t id, uint16_t reg, uint16_t regNum, uint8_t *data, uint16_t size){
289 | uint16_t length = regNum/8 + ((regNum%8) ? 1 : 0);
290 | if(size < length) return (uint8_t)eRTU_EXCEPTION_ILLEGAL_DATA_VALUE;
291 | //#if defined(ESP8266)
292 | uint8_t temp[size + 5];
293 | temp[0] = (uint8_t)((reg >> 8) & 0xFF);
294 | temp[1] = (uint8_t)(reg & 0xFF);
295 | temp[2] = (uint8_t)((regNum >> 8) & 0xFF);
296 | temp[3] = (uint8_t)(regNum & 0xFF);
297 | temp[4] = (uint8_t)length;
298 | //#else
299 | //uint8_t temp[size + 5] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF),(uint8_t)((regNum >> 8) & 0xFF), (uint8_t)(regNum & 0xFF),(uint8_t)length};
300 | //#endif
301 | uint8_t ret = 0;
302 | if(id > 0xF7){
303 | RTU_DBG("Device id error");
304 | return (uint8_t)eRTU_ID_ERROR;
305 | }
306 | memcpy(temp+5, data, size);
307 | pRtuPacketHeader_t header = packed(id, eCMD_WRITE_MULTI_COILS, temp, sizeof(temp));
308 | sendPackage(header);
309 | header = recvAndParsePackage(id, (uint8_t)eCMD_WRITE_MULTI_COILS, reg, &ret);
310 | size = 0;
311 | if((ret == 0) && (header != NULL)){
312 | size = (((uint8_t *)header->payload)[2] << 8) | ((uint8_t *)header->payload)[3];
313 | free(header);
314 | }
315 | return ret;
316 | }
317 | uint8_t DFRobot_RTU::writeHoldingRegister(uint8_t id, uint16_t reg, void *data, uint16_t size){
318 | if(((size % 2) != 0) || (size > 250) || data == NULL) return (uint8_t)eRTU_EXCEPTION_ILLEGAL_DATA_VALUE;
319 | //#if defined(ESP8266)
320 | uint8_t temp[size + 5];
321 | temp[0] = (uint8_t)((reg >> 8) & 0xFF);
322 | temp[1] = (uint8_t)(reg & 0xFF);
323 | temp[2] = (uint8_t)(((size/2) >> 8) & 0xFF);
324 | temp[3] = (uint8_t)((size/2) & 0xFF);
325 | temp[4] = (uint8_t)size;
326 | //#else
327 | //uint8_t temp[size + 5] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)(((size/2) >> 8) & 0xFF), (uint8_t)((size/2) & 0xFF),(uint8_t)size};
328 | //#endif
329 | uint8_t ret = 0;
330 | if(id > 0xF7){
331 | RTU_DBG("Device id error");
332 | return (uint8_t)eRTU_ID_ERROR;
333 | }
334 | memcpy(temp+5, data, size);
335 | pRtuPacketHeader_t header = packed(id, eCMD_WRITE_MULTI_HOLDING, temp, sizeof(temp));
336 | sendPackage(header);
337 | header = recvAndParsePackage(id, (uint8_t)eCMD_WRITE_MULTI_HOLDING, reg, &ret);
338 | size = 0;
339 | if((ret == 0) && (header != NULL)){
340 | size = (((uint8_t *)header->payload)[2] << 8) | ((uint8_t *)header->payload)[3];
341 | free(header);
342 | }
343 | return ret;
344 | }
345 |
346 | uint8_t DFRobot_RTU::writeHoldingRegister(uint8_t id, uint16_t reg, uint16_t *data, uint16_t regNum){
347 | uint16_t size = regNum * 2;
348 | uint8_t *pBuf = (uint8_t *)data;
349 | //#if defined(ESP8266)
350 | uint8_t temp[size + 5];
351 | temp[0] = (uint8_t)((reg >> 8) & 0xFF);
352 | temp[1] = (uint8_t)(reg & 0xFF);
353 | temp[2] = (uint8_t)(((size/2) >> 8) & 0xFF);
354 | temp[3] = (uint8_t)((size/2) & 0xFF);
355 | temp[4] = (uint8_t)size;
356 | //#else
357 | //uint8_t temp[size + 5] = {(uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF), (uint8_t)(((size/2) >> 8) & 0xFF), (uint8_t)((size/2) & 0xFF),(uint8_t)size};
358 | //#endif
359 | uint8_t ret = 0;
360 | if(id > 0xF7){
361 | RTU_DBG("Device id error");
362 | return (uint8_t)eRTU_ID_ERROR;
363 | }
364 | //memcpy(temp+5, data, size);
365 | for(int i = 0; i < regNum; i++){
366 | temp[5+i*2] = pBuf[2*i + 1];
367 | temp[6+i*2] = pBuf[2*i] ;
368 | }
369 | pRtuPacketHeader_t header = packed(id, eCMD_WRITE_MULTI_HOLDING, temp, sizeof(temp));
370 | sendPackage(header);
371 | header = recvAndParsePackage(id, (uint8_t)eCMD_WRITE_MULTI_HOLDING, reg, &ret);
372 | size = 0;
373 | if((ret == 0) && (header != NULL)){
374 | size = (((uint8_t *)header->payload)[2] << 8) | ((uint8_t *)header->payload)[3];
375 | free(header);
376 | }
377 | return ret;
378 | }
379 |
380 | DFRobot_RTU::pRtuPacketHeader_t DFRobot_RTU::packed(uint8_t id, eFunctionCommand_t cmd, void *data, uint16_t size){
381 | return packed(id, (uint8_t)cmd, data, size);
382 | }
383 |
384 | DFRobot_RTU::pRtuPacketHeader_t DFRobot_RTU::packed(uint8_t id, uint8_t cmd, void *data, uint16_t size){
385 | pRtuPacketHeader_t header = NULL;
386 | uint16_t crc = 0;
387 | if((data == NULL) || (size == 0)) return NULL;
388 | if((header = (pRtuPacketHeader_t)malloc(sizeof(sRtuPacketHeader_t) + size)) == NULL){
389 | RTU_DBG("Memory ERROR");
390 | return NULL;
391 | }
392 | header->len = sizeof(sRtuPacketHeader_t) + size - 2;
393 | header->id = id;
394 | header->cmd = cmd;
395 | memcpy(header->payload, data, size);
396 | crc = calculateCRC((uint8_t *)&(header->id), (header->len) - 2);
397 | ((uint8_t *)header->payload)[size] = (crc >> 8) & 0xFF;
398 | ((uint8_t *)header->payload)[size+1] = crc & 0xFF;
399 | return header;
400 | }
401 |
402 | void DFRobot_RTU::sendPackage(pRtuPacketHeader_t header){
403 | clearRecvBuffer();
404 | if(header != NULL){
405 | if(_dePin>0){
406 | digitalWrite(_dePin,HIGH);
407 | delayMicroseconds(50);
408 | }
409 | _s->write((uint8_t *)&(header->id), header->len);
410 | _s->flush();
411 |
412 | free(header);
413 | if(_dePin>0){
414 | //delayMicroseconds(50);
415 | digitalWrite(_dePin,LOW);
416 | }
417 | }
418 | }
419 |
420 | DFRobot_RTU::pRtuPacketHeader_t DFRobot_RTU::recvAndParsePackage(uint8_t id, uint8_t cmd, uint16_t data, uint8_t *error){
421 | if(id > 0xF7) return NULL;
422 | if(id == 0){
423 | if (error != NULL) *error = 0;
424 | return NULL;
425 | }
426 |
427 | uint8_t head[4] = {0, 0, 0, 0};
428 | uint16_t crc = 0;
429 | pRtuPacketHeader_t header = NULL;
430 | //uint8_t timeInterMs = 5;
431 |
432 | LOOP:
433 | uint16_t remain, index = 0;
434 | uint32_t time = millis();
435 | for(int i = 0; i < 4;){
436 | if(_s->available()){
437 | head[index++] = (uint8_t)_s->read();
438 | RTU_DBG(head[index-1],HEX);
439 | if((index == 1) && (head[0] != id)){
440 | index = 0;
441 | }else if((index == 2) && ((head[1]&0x7F) != cmd)){
442 | index = 0;
443 | }
444 | i = index;
445 | time = millis();
446 | }
447 | if((millis() - time) > _timeout) {
448 | RTU_DBG("ERROR");
449 | RTU_DBG(millis() - time);
450 | break;
451 | }
452 | }
453 |
454 | if(index != 4) {
455 | RTU_DBG();
456 | if(error != NULL) *error = eRTU_RECV_ERROR;
457 | return NULL;
458 | }
459 | switch(head[1]){
460 | case eCMD_READ_COILS:
461 | case eCMD_READ_DISCRETE:
462 | case eCMD_READ_HOLDING:
463 | case eCMD_READ_INPUT:
464 | if(head[2] != (data & 0xFF)) {
465 | index = 0;
466 | goto LOOP;
467 | }
468 | index = 5 + head[2];
469 | break;
470 | case eCMD_WRITE_COILS:
471 | case eCMD_WRITE_HOLDING:
472 | case eCMD_WRITE_MULTI_COILS:
473 | case eCMD_WRITE_MULTI_HOLDING:
474 | if(((head[2] << 8) | (head[3])) != data){
475 | index = 0;
476 | goto LOOP;
477 | }
478 | index = 8;
479 | break;
480 | default:
481 | index = 5;
482 | break;
483 | }
484 | if((header = (pRtuPacketHeader_t)malloc(index+2)) == NULL){
485 | RTU_DBG("Memory ERROR");
486 | if(error != NULL) *error = eRTU_RECV_ERROR;
487 | return NULL;
488 | }
489 | header->len = index;
490 | memcpy((uint8_t *)&(header->id), head, 4);
491 | remain = index - 4;
492 | index = 2;
493 | time = millis();
494 | while(remain){
495 | RTU_DBG(_s->available());
496 | if(_s->available()){
497 | *(header->payload+index) = (uint8_t)_s->read();
498 | index++;
499 | time = millis();
500 | remain--;
501 | }
502 | if((millis() - time) > _timeout) {
503 | free(header);
504 | if(error != NULL) *error = eRTU_RECV_ERROR;
505 | RTU_DBG();
506 | return NULL;
507 | }
508 | }
509 | crc = (((uint8_t *)header->payload)[(header->len)-4] << 8) | ((uint8_t *)header->payload)[(header->len)-3];
510 |
511 | if(crc != calculateCRC((uint8_t *)&(header->id), (header->len) - 2)){
512 | free(header);
513 | RTU_DBG("CRC ERROR");
514 | if(error != NULL) *error = eRTU_RECV_ERROR;
515 | return NULL;
516 | }
517 | if(error != NULL) *error = 0;
518 | if(head[1] & 0x80){
519 | *error = head[2];
520 | }
521 |
522 | return header;
523 |
524 | }
525 |
526 |
527 | uint16_t DFRobot_RTU::calculateCRC(uint8_t *data, uint8_t len){
528 | uint16_t crc = 0xFFFF;
529 | for( uint8_t pos = 0; pos < len; pos++){
530 | crc ^= (uint16_t)data[ pos ];
531 | for(uint8_t i = 8; i != 0; i--){
532 | if((crc & 0x0001) != 0){
533 | crc >>= 1;
534 | crc ^= 0xA001;
535 | }else{
536 | crc >>= 1;
537 | }
538 | }
539 | }
540 | crc = ((crc & 0x00FF) << 8) | ((crc & 0xFF00) >> 8);
541 | return crc;
542 | }
543 |
544 | void DFRobot_RTU::clearRecvBuffer(){
545 | while(_s->available()){
546 | _s->read();
547 | delay(2);
548 | }
549 | }
550 |
--------------------------------------------------------------------------------
/src/DFRobot_RTU.h:
--------------------------------------------------------------------------------
1 | /*!
2 | * @file DFRobot_RTU.h
3 | * @brief Modbus RTU libary for Arduino.
4 | *
5 | * @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
6 | * @licence The MIT License (MIT)
7 | * @author [Arya](xue.peng@dfrobot.com)
8 | * @version V1.0
9 | * @date 2021-07-16
10 | * @https://github.com/DFRobot/DFRobot_RTU
11 | */
12 | #ifndef __DFRobot_RTU_H
13 | #define __DFRobot_RTU_H
14 |
15 | #if ARDUINO >= 100
16 | #include "Arduino.h"
17 | #else
18 | #include "WProgram.h"
19 | #endif
20 |
21 | #include
22 |
23 | //Define RTU_DBG, change 0 to 1 open the RTU_DBG, 1 to 0 to close.
24 | #if 0
25 | #define RTU_DBG(...) {Serial.print("["); Serial.print(__FUNCTION__); Serial.print("(): "); Serial.print(__LINE__); Serial.print(" ] "); Serial.println(__VA_ARGS__);}
26 | #else
27 | #define RTU_DBG(...)
28 | #endif
29 |
30 |
31 | #ifndef RTU_BROADCAST_ADDRESS
32 | #define RTU_BROADCAST_ADDRESS 0x00 /**