├── 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 | ![正反面svg效果图](https://github.com/Arya11111/DFRobot_MCP23017/blob/master/resources/images/SEN0245svg1.png) 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 | ![正反面svg效果图](https://github.com/Arya11111/DFRobot_MCP23017/blob/master/resources/images/SEN0245svg1.png) 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 | ![正反面svg效果图](https://github.com/Arya11111/DFRobot_MCP23017/blob/master/resources/images/SEN0245svg1.png) 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 | ![正反面svg效果图](https://github.com/Arya11111/DFRobot_MCP23017/blob/master/resources/images/SEN0245svg1.png) 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 /**