├── .gitattributes ├── .gitignore ├── ESP8266ModbusRTUMaster_example └── ESP8266ModbusRTUMaster.ino ├── ModBusMaster232 ├── ModbusMaster232.cpp ├── ModbusMaster232.h ├── keywords.txt └── keywords.txt~ └── SoftwareSerial ├── SoftwareSerial.cpp ├── SoftwareSerial.h ├── examples ├── SoftwareSerialExample │ └── SoftwareSerialExample.ino ├── TwoPortReceive │ └── TwoPortReceive.ino └── swsertest │ └── swsertest.ino └── keywords.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /ESP8266ModbusRTUMaster_example/ESP8266ModbusRTUMaster.ino: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trialcommand/ESP8266-Modbus-RTU-Master/a843a6115ce3a279a5c276198bb9c43192386db7/ESP8266ModbusRTUMaster_example/ESP8266ModbusRTUMaster.ino -------------------------------------------------------------------------------- /ModBusMaster232/ModbusMaster232.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol). 4 | */ 5 | /* 6 | 7 | ModbusMaster.cpp - Arduino library for communicating with Modbus slaves 8 | over RS232/485 (via RTU protocol). 9 | 10 | This file is part of ModbusMaster. 11 | 12 | ModbusMaster is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation, either version 3 of the License, or 15 | (at your option) any later version. 16 | 17 | ModbusMaster is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License 23 | along with ModbusMaster. If not, see . 24 | 25 | Written by Doc Walker (Rx) 26 | Copyright © 2009-2013 Doc Walker <4-20ma at wvfans dot net> 27 | 28 | 29 | Modified by PDAControl 2016 30 | - Function crc16 31 | - Function makeWord 32 | 33 | 34 | */ 35 | #include "ModbusMaster232.h" 36 | #include // Modbus RTU pins D7(13),D8(15) RX,TX 37 | SoftwareSerial swSer(13, 15, false, 256); 38 | 39 | #include "Arduino.h" 40 | 41 | 42 | /* _____PUBLIC FUNCTIONS_____________________________________________________ */ 43 | 44 | /** 45 | Constructor. 46 | Creates class object using default serial port 0, Modbus slave ID 1. 47 | @ingroup setup 48 | */ 49 | ModbusMaster232::ModbusMaster232(void) 50 | { 51 | _u8SerialPort = 0; 52 | _u8MBSlave = 1; 53 | } 54 | 55 | 56 | /** 57 | Constructor. 58 | 59 | Creates class object using default serial port 0, specified Modbus slave ID. 60 | 61 | @overload void ModbusMaster::ModbusMaster(uint8_t u8MBSlave) 62 | @param u8MBSlave Modbus slave ID (1..255) 63 | @ingroup setup 64 | */ 65 | ModbusMaster232::ModbusMaster232(uint8_t u8MBSlave) 66 | { 67 | _u8SerialPort = 0; 68 | _u8MBSlave = u8MBSlave; 69 | } 70 | 71 | 72 | /** 73 | CRC ESP8266 74 | 75 | Creates class object using default serial port 0, specified Modbus slave ID. 76 | 77 | @overload void ModbusMaster::ModbusMaster(uint8_t u8MBSlave) 78 | @param u8MBSlave Modbus slave ID (1..255) 79 | @ingroup setup 80 | */ 81 | 82 | //Function crc16 created for ESP8266 - PDAControl 83 | //http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__crc_1ga95371c87f25b0a2497d9cba13190847f.html 84 | // append CRC 85 | 86 | uint16_t _crc16_update2 (uint16_t crc, uint8_t a) 87 | { 88 | int i; 89 | 90 | crc ^= a; 91 | for (i = 0; i < 8; ++i) 92 | { 93 | if (crc & 1) 94 | crc = (crc >> 1) ^ 0xA001; 95 | else 96 | crc = (crc >> 1); 97 | } 98 | 99 | return crc; 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | /** 112 | Initialize class object. 113 | 114 | Sets up the serial port using default 19200 baud rate. 115 | Call once class has been instantiated, typically within setup(). 116 | 117 | @ingroup setup 118 | */ 119 | void ModbusMaster232::begin(void) 120 | { 121 | swSer.begin(9600); 122 | } 123 | 124 | 125 | /** 126 | Initialize class object. 127 | 128 | Sets up the swSer port using specified baud rate. 129 | Call once class has been instantiated, typically within setup(). 130 | 131 | @overload ModbusMaster::begin(uint16_t u16BaudRate) 132 | @param u16BaudRate baud rate, in standard increments (300..115200) 133 | @ingroup setup 134 | */ 135 | void ModbusMaster232::begin(unsigned long BaudRate ) 136 | { 137 | // txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t)); 138 | _u8TransmitBufferIndex = 0; 139 | u16TransmitBufferLength = 0; 140 | 141 | delay(100); 142 | swSer.begin(BaudRate); 143 | } 144 | 145 | 146 | void ModbusMaster232::beginTransmission(uint16_t u16Address) 147 | { 148 | _u16WriteAddress = u16Address; 149 | _u8TransmitBufferIndex = 0; 150 | u16TransmitBufferLength = 0; 151 | } 152 | 153 | // eliminate this function in favor of using existing MB request functions 154 | uint8_t ModbusMaster232::requestFrom(uint16_t address, uint16_t quantity) 155 | { 156 | uint8_t read; 157 | // clamp to buffer length 158 | if(quantity > ku8MaxBufferSize) 159 | { 160 | quantity = ku8MaxBufferSize; 161 | } 162 | // set rx buffer iterator vars 163 | _u8ResponseBufferIndex = 0; 164 | _u8ResponseBufferLength = read; 165 | 166 | return read; 167 | } 168 | 169 | 170 | void ModbusMaster232::sendBit(bool data) 171 | { 172 | uint8_t txBitIndex = u16TransmitBufferLength % 16; 173 | if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize) 174 | { 175 | if (0 == txBitIndex) 176 | { 177 | _u16TransmitBuffer[_u8TransmitBufferIndex] = 0; 178 | } 179 | bitWrite(_u16TransmitBuffer[_u8TransmitBufferIndex], txBitIndex, data); 180 | u16TransmitBufferLength++; 181 | _u8TransmitBufferIndex = u16TransmitBufferLength >> 4; 182 | } 183 | } 184 | 185 | 186 | void ModbusMaster232::send(uint16_t data) 187 | { 188 | if (_u8TransmitBufferIndex < ku8MaxBufferSize) 189 | { 190 | _u16TransmitBuffer[_u8TransmitBufferIndex++] = data; 191 | u16TransmitBufferLength = _u8TransmitBufferIndex << 4; 192 | } 193 | } 194 | 195 | 196 | void ModbusMaster232::send(uint32_t data) 197 | { 198 | send(lowWord(data)); 199 | send(highWord(data)); 200 | } 201 | 202 | 203 | void ModbusMaster232::send(uint8_t data) 204 | { 205 | send(uint16_t(data)); 206 | } 207 | 208 | 209 | 210 | uint8_t ModbusMaster232::available(void) 211 | { 212 | return _u8ResponseBufferLength - _u8ResponseBufferIndex; 213 | } 214 | 215 | 216 | uint16_t ModbusMaster232::receive(void) 217 | { 218 | if (_u8ResponseBufferIndex < _u8ResponseBufferLength) 219 | { 220 | return _u16ResponseBuffer[_u8ResponseBufferIndex++]; 221 | } 222 | else 223 | { 224 | return 0xFFFF; 225 | } 226 | } 227 | 228 | 229 | 230 | 231 | /** 232 | Set idle time callback function (cooperative multitasking). 233 | 234 | This function gets called in the idle time between transmission of data 235 | and response from slave. Do not call functions that read from the serial 236 | buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other 237 | serial ports, etc. is permitted within callback function. 238 | 239 | @see ModbusMaster::ModbusMasterTransaction() 240 | */ 241 | void ModbusMaster232::idle(void (*idle)()) 242 | { 243 | _idle = idle; 244 | } 245 | 246 | 247 | /** 248 | Retrieve data from response buffer. 249 | 250 | @see ModbusMaster::clearResponseBuffer() 251 | @param u8Index index of response buffer array (0x00..0x3F) 252 | @return value in position u8Index of response buffer (0x0000..0xFFFF) 253 | @ingroup buffer 254 | */ 255 | uint16_t ModbusMaster232::getResponseBuffer(uint8_t u8Index) 256 | { 257 | if (u8Index < ku8MaxBufferSize) 258 | { 259 | return _u16ResponseBuffer[u8Index]; 260 | } 261 | else 262 | { 263 | return 0xFFFF; 264 | } 265 | } 266 | 267 | 268 | /** 269 | Clear Modbus response buffer. 270 | 271 | @see ModbusMaster::getResponseBuffer(uint8_t u8Index) 272 | @ingroup buffer 273 | */ 274 | void ModbusMaster232::clearResponseBuffer() 275 | { 276 | uint8_t i; 277 | 278 | for (i = 0; i < ku8MaxBufferSize; i++) 279 | { 280 | _u16ResponseBuffer[i] = 0; 281 | } 282 | } 283 | 284 | 285 | /** 286 | Place data in transmit buffer. 287 | 288 | @see ModbusMaster::clearTransmitBuffer() 289 | @param u8Index index of transmit buffer array (0x00..0x3F) 290 | @param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF) 291 | @return 0 on success; exception number on failure 292 | @ingroup buffer 293 | */ 294 | uint8_t ModbusMaster232::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) 295 | { 296 | if (u8Index < ku8MaxBufferSize) 297 | { 298 | _u16TransmitBuffer[u8Index] = u16Value; 299 | return ku8MBSuccess; 300 | } 301 | else 302 | { 303 | return ku8MBIllegalDataAddress; 304 | } 305 | } 306 | 307 | 308 | /** 309 | Clear Modbus transmit buffer. 310 | 311 | @see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) 312 | @ingroup buffer 313 | */ 314 | void ModbusMaster232::clearTransmitBuffer() 315 | { 316 | uint8_t i; 317 | 318 | for (i = 0; i < ku8MaxBufferSize; i++) 319 | { 320 | _u16TransmitBuffer[i] = 0; 321 | } 322 | } 323 | 324 | 325 | /** 326 | Modbus function 0x01 Read Coils. 327 | 328 | This function code is used to read from 1 to 2000 contiguous status of 329 | coils in a remote device. The request specifies the starting address, 330 | i.e. the address of the first coil specified, and the number of coils. 331 | Coils are addressed starting at zero. 332 | 333 | The coils in the response buffer are packed as one coil per bit of the 334 | data field. Status is indicated as 1=ON and 0=OFF. The LSB of the first 335 | data word contains the output addressed in the query. The other coils 336 | follow toward the high order end of this word and from low order to high 337 | order in subsequent words. 338 | 339 | If the returned quantity is not a multiple of sixteen, the remaining 340 | bits in the final data word will be padded with zeros (toward the high 341 | order end of the word). 342 | 343 | @param u16ReadAddress address of first coil (0x0000..0xFFFF) 344 | @param u16BitQty quantity of coils to read (1..2000, enforced by remote device) 345 | @return 0 on success; exception number on failure 346 | @ingroup discrete 347 | */ 348 | uint8_t ModbusMaster232::readCoils(uint16_t u16ReadAddress, uint16_t u16BitQty) 349 | { 350 | _u16ReadAddress = u16ReadAddress; 351 | _u16ReadQty = u16BitQty; 352 | return ModbusMasterTransaction(ku8MBReadCoils); 353 | } 354 | 355 | 356 | /** 357 | Modbus function 0x02 Read Discrete Inputs. 358 | 359 | This function code is used to read from 1 to 2000 contiguous status of 360 | discrete inputs in a remote device. The request specifies the starting 361 | address, i.e. the address of the first input specified, and the number 362 | of inputs. Discrete inputs are addressed starting at zero. 363 | 364 | The discrete inputs in the response buffer are packed as one input per 365 | bit of the data field. Status is indicated as 1=ON; 0=OFF. The LSB of 366 | the first data word contains the input addressed in the query. The other 367 | inputs follow toward the high order end of this word, and from low order 368 | to high order in subsequent words. 369 | 370 | If the returned quantity is not a multiple of sixteen, the remaining 371 | bits in the final data word will be padded with zeros (toward the high 372 | order end of the word). 373 | 374 | @param u16ReadAddress address of first discrete input (0x0000..0xFFFF) 375 | @param u16BitQty quantity of discrete inputs to read (1..2000, enforced by remote device) 376 | @return 0 on success; exception number on failure 377 | @ingroup discrete 378 | */ 379 | uint8_t ModbusMaster232::readDiscreteInputs(uint16_t u16ReadAddress, 380 | uint16_t u16BitQty) 381 | { 382 | _u16ReadAddress = u16ReadAddress; 383 | _u16ReadQty = u16BitQty; 384 | return ModbusMasterTransaction(ku8MBReadDiscreteInputs); 385 | } 386 | 387 | 388 | /** 389 | Modbus function 0x03 Read Holding Registers. 390 | 391 | This function code is used to read the contents of a contiguous block of 392 | holding registers in a remote device. The request specifies the starting 393 | register address and the number of registers. Registers are addressed 394 | starting at zero. 395 | 396 | The register data in the response buffer is packed as one word per 397 | register. 398 | 399 | @param u16ReadAddress address of the first holding register (0x0000..0xFFFF) 400 | @param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) 401 | @return 0 on success; exception number on failure 402 | @ingroup register 403 | */ 404 | uint8_t ModbusMaster232::readHoldingRegisters(uint16_t u16ReadAddress, 405 | uint16_t u16ReadQty) 406 | { 407 | _u16ReadAddress = u16ReadAddress; 408 | _u16ReadQty = u16ReadQty; 409 | return ModbusMasterTransaction(ku8MBReadHoldingRegisters); 410 | } 411 | 412 | 413 | /** 414 | Modbus function 0x04 Read Input Registers. 415 | 416 | This function code is used to read from 1 to 125 contiguous input 417 | registers in a remote device. The request specifies the starting 418 | register address and the number of registers. Registers are addressed 419 | starting at zero. 420 | 421 | The register data in the response buffer is packed as one word per 422 | register. 423 | 424 | @param u16ReadAddress address of the first input register (0x0000..0xFFFF) 425 | @param u16ReadQty quantity of input registers to read (1..125, enforced by remote device) 426 | @return 0 on success; exception number on failure 427 | @ingroup register 428 | */ 429 | uint8_t ModbusMaster232::readInputRegisters(uint16_t u16ReadAddress, 430 | uint8_t u16ReadQty) 431 | { 432 | _u16ReadAddress = u16ReadAddress; 433 | _u16ReadQty = u16ReadQty; 434 | return ModbusMasterTransaction(ku8MBReadInputRegisters); 435 | } 436 | 437 | 438 | /** 439 | Modbus function 0x05 Write Single Coil. 440 | 441 | This function code is used to write a single output to either ON or OFF 442 | in a remote device. The requested ON/OFF state is specified by a 443 | constant in the state field. A non-zero value requests the output to be 444 | ON and a value of 0 requests it to be OFF. The request specifies the 445 | address of the coil to be forced. Coils are addressed starting at zero. 446 | 447 | @param u16WriteAddress address of the coil (0x0000..0xFFFF) 448 | @param u8State 0=OFF, non-zero=ON (0x00..0xFF) 449 | @return 0 on success; exception number on failure 450 | @ingroup discrete 451 | */ 452 | uint8_t ModbusMaster232::writeSingleCoil(uint16_t u16WriteAddress, uint8_t u8State) 453 | { 454 | _u16WriteAddress = u16WriteAddress; 455 | _u16WriteQty = (u8State ? 0xFF00 : 0x0000); 456 | return ModbusMasterTransaction(ku8MBWriteSingleCoil); 457 | } 458 | 459 | 460 | /** 461 | Modbus function 0x06 Write Single Register. 462 | 463 | This function code is used to write a single holding register in a 464 | remote device. The request specifies the address of the register to be 465 | written. Registers are addressed starting at zero. 466 | 467 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF) 468 | @param u16WriteValue value to be written to holding register (0x0000..0xFFFF) 469 | @return 0 on success; exception number on failure 470 | @ingroup register 471 | */ 472 | uint8_t ModbusMaster232::writeSingleRegister(uint16_t u16WriteAddress, 473 | uint16_t u16WriteValue) 474 | { 475 | _u16WriteAddress = u16WriteAddress; 476 | _u16WriteQty = 0; 477 | _u16TransmitBuffer[0] = u16WriteValue; 478 | return ModbusMasterTransaction(ku8MBWriteSingleRegister); 479 | } 480 | 481 | 482 | /** 483 | Modbus function 0x0F Write Multiple Coils. 484 | 485 | This function code is used to force each coil in a sequence of coils to 486 | either ON or OFF in a remote device. The request specifies the coil 487 | references to be forced. Coils are addressed starting at zero. 488 | 489 | The requested ON/OFF states are specified by contents of the transmit 490 | buffer. A logical '1' in a bit position of the buffer requests the 491 | corresponding output to be ON. A logical '0' requests it to be OFF. 492 | 493 | @param u16WriteAddress address of the first coil (0x0000..0xFFFF) 494 | @param u16BitQty quantity of coils to write (1..2000, enforced by remote device) 495 | @return 0 on success; exception number on failure 496 | @ingroup discrete 497 | */ 498 | uint8_t ModbusMaster232::writeMultipleCoils(uint16_t u16WriteAddress, 499 | uint16_t u16BitQty) 500 | { 501 | _u16WriteAddress = u16WriteAddress; 502 | _u16WriteQty = u16BitQty; 503 | return ModbusMasterTransaction(ku8MBWriteMultipleCoils); 504 | } 505 | uint8_t ModbusMaster232::writeMultipleCoils() 506 | { 507 | _u16WriteQty = u16TransmitBufferLength; 508 | return ModbusMasterTransaction(ku8MBWriteMultipleCoils); 509 | } 510 | 511 | 512 | /** 513 | Modbus function 0x10 Write Multiple Registers. 514 | 515 | This function code is used to write a block of contiguous registers (1 516 | to 123 registers) in a remote device. 517 | 518 | The requested written values are specified in the transmit buffer. Data 519 | is packed as one word per register. 520 | 521 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF) 522 | @param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device) 523 | @return 0 on success; exception number on failure 524 | @ingroup register 525 | */ 526 | uint8_t ModbusMaster232::writeMultipleRegisters(uint16_t u16WriteAddress, 527 | uint16_t u16WriteQty) 528 | { 529 | _u16WriteAddress = u16WriteAddress; 530 | _u16WriteQty = u16WriteQty; 531 | return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); 532 | } 533 | 534 | // new version based on Wire.h 535 | uint8_t ModbusMaster232::writeMultipleRegisters() 536 | { 537 | _u16WriteQty = _u8TransmitBufferIndex; 538 | return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); 539 | } 540 | 541 | 542 | /** 543 | Modbus function 0x16 Mask Write Register. 544 | 545 | This function code is used to modify the contents of a specified holding 546 | register using a combination of an AND mask, an OR mask, and the 547 | register's current contents. The function can be used to set or clear 548 | individual bits in the register. 549 | 550 | The request specifies the holding register to be written, the data to be 551 | used as the AND mask, and the data to be used as the OR mask. Registers 552 | are addressed starting at zero. 553 | 554 | The function's algorithm is: 555 | 556 | Result = (Current Contents && And_Mask) || (Or_Mask && (~And_Mask)) 557 | 558 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF) 559 | @param u16AndMask AND mask (0x0000..0xFFFF) 560 | @param u16OrMask OR mask (0x0000..0xFFFF) 561 | @return 0 on success; exception number on failure 562 | @ingroup register 563 | */ 564 | uint8_t ModbusMaster232::maskWriteRegister(uint16_t u16WriteAddress, 565 | uint16_t u16AndMask, uint16_t u16OrMask) 566 | { 567 | _u16WriteAddress = u16WriteAddress; 568 | _u16TransmitBuffer[0] = u16AndMask; 569 | _u16TransmitBuffer[1] = u16OrMask; 570 | return ModbusMasterTransaction(ku8MBMaskWriteRegister); 571 | } 572 | 573 | 574 | /** 575 | Modbus function 0x17 Read Write Multiple Registers. 576 | 577 | This function code performs a combination of one read operation and one 578 | write operation in a single MODBUS transaction. The write operation is 579 | performed before the read. Holding registers are addressed starting at 580 | zero. 581 | 582 | The request specifies the starting address and number of holding 583 | registers to be read as well as the starting address, and the number of 584 | holding registers. The data to be written is specified in the transmit 585 | buffer. 586 | 587 | @param u16ReadAddress address of the first holding register (0x0000..0xFFFF) 588 | @param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) 589 | @param u16WriteAddress address of the first holding register (0x0000..0xFFFF) 590 | @param u16WriteQty quantity of holding registers to write (1..121, enforced by remote device) 591 | @return 0 on success; exception number on failure 592 | @ingroup register 593 | */ 594 | uint8_t ModbusMaster232::readWriteMultipleRegisters(uint16_t u16ReadAddress, 595 | uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty) 596 | { 597 | _u16ReadAddress = u16ReadAddress; 598 | _u16ReadQty = u16ReadQty; 599 | _u16WriteAddress = u16WriteAddress; 600 | _u16WriteQty = u16WriteQty; 601 | return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters); 602 | } 603 | uint8_t ModbusMaster232::readWriteMultipleRegisters(uint16_t u16ReadAddress, 604 | uint16_t u16ReadQty) 605 | { 606 | _u16ReadAddress = u16ReadAddress; 607 | _u16ReadQty = u16ReadQty; 608 | _u16WriteQty = _u8TransmitBufferIndex; 609 | return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters); 610 | } 611 | 612 | 613 | /* _____PRIVATE FUNCTIONS____________________________________________________ */ 614 | /** 615 | Modbus transaction engine. 616 | Sequence: 617 | - assemble Modbus Request Application Data Unit (ADU), 618 | based on particular function called 619 | - transmit request over selected serial port 620 | - wait for/retrieve response 621 | - evaluate/disassemble response 622 | - return status (success/exception) 623 | 624 | @param u8MBFunction Modbus function (0x01..0xFF) 625 | @return 0 on success; exception number on failure 626 | */ 627 | uint8_t ModbusMaster232::ModbusMasterTransaction(uint8_t u8MBFunction) 628 | { 629 | uint8_t u8ModbusADU[256]; 630 | uint8_t u8ModbusADUSize = 0; 631 | uint8_t i, u8Qty; 632 | uint16_t u16CRC; 633 | uint32_t u32StartTime; 634 | uint8_t u8BytesLeft = 8; 635 | uint8_t u8MBStatus = ku8MBSuccess; 636 | 637 | // assemble Modbus Request Application Data Unit 638 | u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave; 639 | u8ModbusADU[u8ModbusADUSize++] = u8MBFunction; 640 | 641 | switch(u8MBFunction) 642 | { 643 | case ku8MBReadCoils: 644 | case ku8MBReadDiscreteInputs: 645 | case ku8MBReadInputRegisters: 646 | case ku8MBReadHoldingRegisters: 647 | case ku8MBReadWriteMultipleRegisters: 648 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress); 649 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress); 650 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty); 651 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty); 652 | break; 653 | } 654 | 655 | switch(u8MBFunction) 656 | { 657 | case ku8MBWriteSingleCoil: 658 | case ku8MBMaskWriteRegister: 659 | case ku8MBWriteMultipleCoils: 660 | case ku8MBWriteSingleRegister: 661 | case ku8MBWriteMultipleRegisters: 662 | case ku8MBReadWriteMultipleRegisters: 663 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress); 664 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress); 665 | break; 666 | } 667 | 668 | switch(u8MBFunction) 669 | { 670 | case ku8MBWriteSingleCoil: 671 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); 672 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); 673 | break; 674 | 675 | case ku8MBWriteSingleRegister: 676 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]); 677 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]); 678 | break; 679 | 680 | case ku8MBWriteMultipleCoils: 681 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); 682 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); 683 | u8Qty = (_u16WriteQty % 8) ? ((_u16WriteQty >> 3) + 1) : (_u16WriteQty >> 3); 684 | u8ModbusADU[u8ModbusADUSize++] = u8Qty; 685 | for (i = 0; i < u8Qty; i++) 686 | { 687 | switch(i % 2) 688 | { 689 | case 0: // i is even 690 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]); 691 | break; 692 | 693 | case 1: // i is odd 694 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]); 695 | break; 696 | } 697 | } 698 | break; 699 | 700 | case ku8MBWriteMultipleRegisters: 701 | case ku8MBReadWriteMultipleRegisters: 702 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); 703 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); 704 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1); 705 | 706 | for (i = 0; i < lowByte(_u16WriteQty); i++) 707 | { 708 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]); 709 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]); 710 | } 711 | break; 712 | 713 | case ku8MBMaskWriteRegister: 714 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]); 715 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]); 716 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[1]); 717 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]); 718 | break; 719 | } 720 | 721 | 722 | u16CRC = 0xFFFF; 723 | for (i = 0; i < u8ModbusADUSize; i++) 724 | { 725 | //Function crc16 for ESP8266 - PDAControl 726 | u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]); 727 | } 728 | u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC); 729 | u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC); 730 | u8ModbusADU[u8ModbusADUSize] = 0; 731 | 732 | // transmit request 733 | for (i = 0; i < u8ModbusADUSize; i++) 734 | { 735 | swSer.print(char(u8ModbusADU[i])); 736 | } 737 | 738 | delay(2); 739 | 740 | u8ModbusADUSize = 1; 741 | 742 | // loop until we run out of time or bytes, or an error occurs 743 | u32StartTime = millis(); 744 | 745 | int val = 0xFF; 746 | long cont = 0; 747 | 748 | while((val != _u8MBSlave) && (cont < 100)) { 749 | val = swSer.read(); 750 | delay(5); 751 | cont ++; 752 | } 753 | 754 | u8ModbusADU[u8ModbusADUSize] = val; 755 | 756 | 757 | while (u8BytesLeft && !u8MBStatus) 758 | { 759 | if (swSer.available()) 760 | { 761 | u8ModbusADU[u8ModbusADUSize] = swSer.read(); 762 | u8BytesLeft--; 763 | u8ModbusADUSize ++; 764 | 765 | } 766 | else 767 | { 768 | 769 | if (_idle) 770 | { 771 | _idle(); 772 | } 773 | 774 | } 775 | 776 | // evaluate slave ID, function code once enough bytes have been read 777 | if (u8ModbusADUSize == 5) 778 | { 779 | 780 | // verify response is for correct Modbus slave 781 | if (u8ModbusADU[0] != _u8MBSlave) 782 | { 783 | u8MBStatus = ku8MBInvalidSlaveID; 784 | break; 785 | } 786 | 787 | // verify response is for correct Modbus function code (mask exception bit 7) 788 | if ((u8ModbusADU[1] & 0x7F) != u8MBFunction) 789 | { 790 | u8MBStatus = ku8MBInvalidFunction; 791 | break; 792 | } 793 | 794 | // check whether Modbus exception occurred; return Modbus Exception Code 795 | if (bitRead(u8ModbusADU[1], 7)) 796 | { 797 | u8MBStatus = u8ModbusADU[2]; 798 | break; 799 | } 800 | 801 | // evaluate returned Modbus function code 802 | switch(u8ModbusADU[1]) 803 | { 804 | case ku8MBReadCoils: 805 | case ku8MBReadDiscreteInputs: 806 | case ku8MBReadInputRegisters: 807 | case ku8MBReadHoldingRegisters: 808 | case ku8MBReadWriteMultipleRegisters: 809 | u8BytesLeft = u8ModbusADU[2]; 810 | break; 811 | 812 | case ku8MBWriteSingleCoil: 813 | case ku8MBWriteMultipleCoils: 814 | case ku8MBWriteSingleRegister: 815 | case ku8MBWriteMultipleRegisters: 816 | u8BytesLeft = 3; 817 | break; 818 | 819 | case ku8MBMaskWriteRegister: 820 | u8BytesLeft = 5; 821 | break; 822 | } 823 | } 824 | if (millis() > (u32StartTime + ku8MBResponseTimeout)) 825 | { 826 | u8MBStatus = ku8MBResponseTimedOut; 827 | } 828 | } 829 | 830 | // verify response is large enough to inspect further 831 | if (!u8MBStatus && u8ModbusADUSize >= 5) 832 | { 833 | // calculate CRC 834 | u16CRC = 0xFFFF; 835 | for (i = 0; i < (u8ModbusADUSize - 2); i++) 836 | { 837 | //Function crc16 for ESP8266 - PDAControl 838 | u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]); 839 | } 840 | 841 | 842 | 843 | 844 | // verify CRC 845 | if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] || 846 | highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1])) 847 | { 848 | u8MBStatus = ku8MBInvalidCRC; 849 | } 850 | } 851 | 852 | // disassemble ADU into words 853 | if (!u8MBStatus) 854 | { 855 | // evaluate returned Modbus function code 856 | switch(u8ModbusADU[1]) 857 | { 858 | case ku8MBReadCoils: 859 | case ku8MBReadDiscreteInputs: 860 | // load bytes into word; response bytes are ordered L, H, L, H, ... 861 | for (i = 0; i < (u8ModbusADU[2] >> 1); i++) 862 | { 863 | if (i < ku8MaxBufferSize) 864 | { 865 | 866 | //Function makeWord Modified to ESP8266 - PDAControl 867 | //modificado for ESP8266 868 | // _u16ResponseBuffer[i] = makeWord(u8ModbusADU[2 * i + 4], u8ModbusADU[2 * i + 3]); 869 | 870 | _u16ResponseBuffer[i] = (u8ModbusADU[2 * i + 4] << 8) | u8ModbusADU[2 * i + 3]; 871 | 872 | } 873 | 874 | _u8ResponseBufferLength = i; 875 | } 876 | 877 | // in the event of an odd number of bytes, load last byte into zero-padded word 878 | if (u8ModbusADU[2] % 2) 879 | { 880 | if (i < ku8MaxBufferSize) 881 | { 882 | //Function makeWord Modified to ESP8266 - PDAControl 883 | 884 | // _u16ResponseBuffer[i] = makeWord(0, u8ModbusADU[2 * i + 3]); 885 | 886 | _u16ResponseBuffer[i] = (0 << 8) | u8ModbusADU[2 * i + 3]; 887 | //return (h << 8) | l; 888 | } 889 | 890 | _u8ResponseBufferLength = i + 1; 891 | } 892 | break; 893 | 894 | case ku8MBReadInputRegisters: 895 | case ku8MBReadHoldingRegisters: 896 | case ku8MBReadWriteMultipleRegisters: 897 | // load bytes into word; response bytes are ordered H, L, H, L, ... 898 | for (i = 0; i < (u8ModbusADU[2] >> 1); i++) 899 | { 900 | if (i < ku8MaxBufferSize) 901 | { 902 | //Function makeWord Modified to ESP8266 - PDAControl 903 | 904 | // _u16ResponseBuffer[i] = makeWord(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]); 905 | 906 | //return (h << 8) | l; 907 | _u16ResponseBuffer[i] = (u8ModbusADU[2 * i + 3] << 8) | u8ModbusADU[2 * i + 4]; 908 | } 909 | 910 | _u8ResponseBufferLength = i; 911 | } 912 | break; 913 | } 914 | } 915 | 916 | 917 | _u8TransmitBufferIndex = 0; 918 | u16TransmitBufferLength = 0; 919 | _u8ResponseBufferIndex = 0; 920 | return u8MBStatus; 921 | } 922 | 923 | /* 924 | 925 | MakeWord function deleted -for ESP8266 PDAControl 926 | 927 | 928 | unsigned int ModbusMaster232::makeWord(unsigned int w) 929 | { 930 | return w; 931 | } 932 | 933 | 934 | unsigned int ModbusMaster232::makeWord(uint8_t h, uint8_t l) 935 | { 936 | return (h << 8) | l; 937 | } 938 | */ 939 | void ModbusMaster232::setSlaveAddress(uint8_t u8MBSlave){ 940 | 941 | _u8MBSlave = u8MBSlave; 942 | } 943 | 944 | -------------------------------------------------------------------------------- /ModBusMaster232/ModbusMaster232.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol). 4 | 5 | @defgroup setup ModbusMaster Object Instantiation/Initialization 6 | @defgroup buffer ModbusMaster Buffer Management 7 | @defgroup discrete Modbus Function Codes for Discrete Coils/Inputs 8 | @defgroup register Modbus Function Codes for Holding/Input Registers 9 | @defgroup constant Modbus Function Codes, Exception Codes 10 | */ 11 | /* 12 | 13 | ModbusMaster.h - Arduino library for communicating with Modbus slaves 14 | over RS232/485 (via RTU protocol). 15 | 16 | This file is part of ModbusMaster. 17 | 18 | ModbusMaster is free software: you can redistribute it and/or modify 19 | it under the terms of the GNU General Public License as published by 20 | the Free Software Foundation, either version 3 of the License, or 21 | (at your option) any later version. 22 | 23 | ModbusMaster is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU General Public License for more details. 27 | 28 | You should have received a copy of the GNU General Public License 29 | along with ModbusMaster. If not, see . 30 | 31 | Written by Doc Walker (Rx) 32 | Copyright © 2009-2013 Doc Walker <4-20ma at wvfans dot net> 33 | 34 | 35 | Modified by PDAControl 2016 36 | - Function crc16 37 | - Function makeWord 38 | 39 | */ 40 | 41 | 42 | 43 | #ifndef _MODBUSMASTER232_H_INCLUDED 44 | #define _MODBUSMASTER232_H_INCLUDED 45 | 46 | 47 | /** 48 | @def __MODBUSMASTER_DEBUG__ (1). 49 | Set to 1 to enable debugging features within class: 50 | - pin 4 cycles for each byte read in the Modbus response 51 | - pin 5 cycles for each millisecond timeout during the Modbus response 52 | */ 53 | #define __MODBUSMASTER_DEBUG__ (1) 54 | 55 | 56 | /* _____STANDARD INCLUDES____________________________________________________ */ 57 | 58 | // include types & constants of Wiring core API 59 | 60 | #ifndef inttypes_h 61 | #include 62 | #endif 63 | 64 | 65 | 66 | /* _____UTILITY MACROS_______________________________________________________ */ 67 | /** 68 | @def lowWord(ww) ((uint16_t) ((ww) & 0xFFFF)) 69 | Macro to return low word of a 32-bit integer. 70 | */ 71 | #define lowWord(ww) ((uint16_t) ((ww) & 0xFFFF)) 72 | 73 | 74 | /** 75 | @def highWord(ww) ((uint16_t) ((ww) >> 16)) 76 | Macro to return high word of a 32-bit integer. 77 | */ 78 | #define highWord(ww) ((uint16_t) ((ww) >> 16)) 79 | 80 | 81 | 82 | /** 83 | @def LONG(hi, lo) ((uint32_t) ((hi) << 16 | (lo))) 84 | Macro to generate 32-bit integer from (2) 16-bit words. 85 | */ 86 | #define LONG(hi, lo) ((uint32_t) ((hi) << 16 | (lo))) 87 | 88 | #define lowByte(w) ((uint8_t) ((w) & 0xff)) 89 | #define highByte(w) ((uint8_t) ((w) >> 8)) 90 | 91 | #define bitRead(value, bit) (((value) >> (bit)) & 0x01) 92 | #define bitSet(value, bit) ((value) |= (1UL << (bit))) 93 | #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) 94 | #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) 95 | 96 | 97 | /* 98 | 99 | Commented function crc16 not supported by ESP8266 - PDAControl 100 | //http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__crc_1ga95371c87f25b0a2497d9cba13190847f.html 101 | 102 | _____PROJECT INCLUDES_____________________________________________________ */ 103 | // functions to calculate Modbus Application Data Unit CRC 104 | //#include 105 | 106 | //#include 107 | 108 | 109 | /* _____CLASS DEFINITIONS____________________________________________________ */ 110 | /** 111 | 112 | 113 | 114 | Arduino class library for communicating with Modbus slaves over 115 | RS232/485 (via RTU protocol). 116 | */ 117 | class ModbusMaster232 118 | { 119 | public: 120 | 121 | ModbusMaster232(); 122 | ModbusMaster232(uint8_t); 123 | ModbusMaster232(uint8_t, uint8_t); 124 | 125 | void begin(); 126 | void begin(unsigned long); 127 | void idle(void (*)()); 128 | 129 | // Modbus exception codes 130 | /** 131 | Modbus protocol illegal function exception. 132 | 133 | The function code received in the query is not an allowable action for 134 | the server (or slave). This may be because the function code is only 135 | applicable to newer devices, and was not implemented in the unit 136 | selected. It could also indicate that the server (or slave) is in the 137 | wrong state to process a request of this type, for example because it is 138 | unconfigured and is being asked to return register values. 139 | 140 | @ingroup constant 141 | */ 142 | static const uint8_t ku8MBIllegalFunction = 0x01; 143 | 144 | /** 145 | Modbus protocol illegal data address exception. 146 | 147 | The data address received in the query is not an allowable address for 148 | the server (or slave). More specifically, the combination of reference 149 | number and transfer length is invalid. For a controller with 100 150 | registers, the ADU addresses the first register as 0, and the last one 151 | as 99. If a request is submitted with a starting register address of 96 152 | and a quantity of registers of 4, then this request will successfully 153 | operate (address-wise at least) on registers 96, 97, 98, 99. If a 154 | request is submitted with a starting register address of 96 and a 155 | quantity of registers of 5, then this request will fail with Exception 156 | Code 0x02 "Illegal Data Address" since it attempts to operate on 157 | registers 96, 97, 98, 99 and 100, and there is no register with address 158 | 100. 159 | 160 | @ingroup constant 161 | */ 162 | static const uint8_t ku8MBIllegalDataAddress = 0x02; 163 | 164 | /** 165 | Modbus protocol illegal data value exception. 166 | 167 | A value contained in the query data field is not an allowable value for 168 | server (or slave). This indicates a fault in the structure of the 169 | remainder of a complex request, such as that the implied length is 170 | incorrect. It specifically does NOT mean that a data item submitted for 171 | storage in a register has a value outside the expectation of the 172 | application program, since the MODBUS protocol is unaware of the 173 | significance of any particular value of any particular register. 174 | 175 | @ingroup constant 176 | */ 177 | static const uint8_t ku8MBIllegalDataValue = 0x03; 178 | 179 | /** 180 | Modbus protocol slave device failure exception. 181 | 182 | An unrecoverable error occurred while the server (or slave) was 183 | attempting to perform the requested action. 184 | 185 | @ingroup constant 186 | */ 187 | static const uint8_t ku8MBSlaveDeviceFailure = 0x04; 188 | 189 | // Class-defined success/exception codes 190 | /** 191 | ModbusMaster success. 192 | 193 | Modbus transaction was successful; the following checks were valid: 194 | - slave ID 195 | - function code 196 | - response code 197 | - data 198 | - CRC 199 | 200 | @ingroup constant 201 | */ 202 | static const uint8_t ku8MBSuccess = 0x00; 203 | 204 | /** 205 | ModbusMaster invalid response slave ID exception. 206 | 207 | The slave ID in the response does not match that of the request. 208 | 209 | @ingroup constant 210 | */ 211 | static const uint8_t ku8MBInvalidSlaveID = 0xE0; 212 | 213 | /** 214 | ModbusMaster invalid response function exception. 215 | 216 | The function code in the response does not match that of the request. 217 | 218 | @ingroup constant 219 | */ 220 | static const uint8_t ku8MBInvalidFunction = 0xE1; 221 | 222 | /** 223 | ModbusMaster response timed out exception. 224 | 225 | The entire response was not received within the timeout period, 226 | ModbusMaster::ku8MBResponseTimeout. 227 | 228 | @ingroup constant 229 | */ 230 | static const uint8_t ku8MBResponseTimedOut = 0xE2; 231 | 232 | /** 233 | ModbusMaster invalid response CRC exception. 234 | 235 | The CRC in the response does not match the one calculated. 236 | 237 | @ingroup constant 238 | */ 239 | static const uint8_t ku8MBInvalidCRC = 0xE3; 240 | 241 | uint16_t getResponseBuffer(uint8_t); 242 | void clearResponseBuffer(); 243 | uint8_t setTransmitBuffer(uint8_t, uint16_t); 244 | void clearTransmitBuffer(); 245 | 246 | void beginTransmission(uint16_t); 247 | uint8_t requestFrom(uint16_t, uint16_t); 248 | void sendBit(bool); 249 | void send(uint8_t); 250 | void send(uint16_t); 251 | void send(uint32_t); 252 | void setSlaveAddress(uint8_t); 253 | 254 | uint8_t available(void); 255 | uint16_t receive(void); 256 | 257 | 258 | uint8_t readCoils(uint16_t, uint16_t); 259 | uint8_t readDiscreteInputs(uint16_t, uint16_t); 260 | uint8_t readHoldingRegisters(uint16_t, uint16_t); 261 | uint8_t readInputRegisters(uint16_t, uint8_t); 262 | uint8_t writeSingleCoil(uint16_t, uint8_t); 263 | uint8_t writeSingleRegister(uint16_t, uint16_t); 264 | uint8_t writeMultipleCoils(uint16_t, uint16_t); 265 | uint8_t writeMultipleCoils(); 266 | uint8_t writeMultipleRegisters(uint16_t, uint16_t); 267 | uint8_t writeMultipleRegisters(); 268 | uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t); 269 | uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t); 270 | uint8_t readWriteMultipleRegisters(uint16_t, uint16_t); 271 | 272 | 273 | private: 274 | uint8_t _u8SerialPort; ///< serial port (0..3) initialized in constructor 275 | uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in constructor 276 | uint16_t _u16BaudRate; ///< baud rate (300..115200) initialized in begin() 277 | static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers 278 | uint16_t _u16ReadAddress; ///< slave register from which to read 279 | uint16_t _u16ReadQty; ///< quantity of words to read 280 | uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer() 281 | uint16_t _u16WriteAddress; ///< slave register to which to write 282 | uint16_t _u16WriteQty; ///< quantity of words to write 283 | uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer() 284 | uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx 285 | uint8_t _u8TransmitBufferIndex; 286 | uint16_t u16TransmitBufferLength; 287 | uint16_t* rxBuffer; // from Wire.h -- need to clean this up Rx 288 | uint8_t _u8ResponseBufferIndex; 289 | uint8_t _u8ResponseBufferLength; 290 | 291 | // Modbus function codes for bit access 292 | static const uint8_t ku8MBReadCoils = 0x01; ///< Modbus function 0x01 Read Coils 293 | static const uint8_t ku8MBReadDiscreteInputs = 0x02; ///< Modbus function 0x02 Read Discrete Inputs 294 | static const uint8_t ku8MBWriteSingleCoil = 0x05; ///< Modbus function 0x05 Write Single Coil 295 | static const uint8_t ku8MBWriteMultipleCoils = 0x0F; ///< Modbus function 0x0F Write Multiple Coils 296 | 297 | // Modbus function codes for 16 bit access 298 | static const uint8_t ku8MBReadHoldingRegisters = 0x03; ///< Modbus function 0x03 Read Holding Registers 299 | static const uint8_t ku8MBReadInputRegisters = 0x04; ///< Modbus function 0x04 Read Input Registers 300 | static const uint8_t ku8MBWriteSingleRegister = 0x06; ///< Modbus function 0x06 Write Single Register 301 | static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers 302 | static const uint8_t ku8MBMaskWriteRegister = 0x16; ///< Modbus function 0x16 Mask Write Register 303 | static const uint8_t ku8MBReadWriteMultipleRegisters = 0x17; ///< Modbus function 0x17 Read Write Multiple Registers 304 | 305 | // Modbus timeout [milliseconds] 306 | static const uint8_t ku8MBResponseTimeout = 200; ///< Modbus timeout [milliseconds] 307 | 308 | // master function that conducts Modbus transactions 309 | uint8_t ModbusMasterTransaction(uint8_t u8MBFunction); 310 | 311 | // idle callback function; gets called during idle time between TX and RX 312 | void (*_idle)(); 313 | 314 | 315 | //Commented function makeWord not supported by ESP8266 - PDAControl 316 | //uint16_t makeWord(uint16_t w); 317 | // uint16_t makeWord(uint8_t h, uint8_t l); 318 | 319 | }; 320 | #endif 321 | 322 | /** 323 | @example examples/Basic/Basic.pde 324 | @example examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde 325 | */ 326 | -------------------------------------------------------------------------------- /ModBusMaster232/keywords.txt: -------------------------------------------------------------------------------- 1 | ModbusMaster232 KEYWORD1 2 | node KEYWORD3 3 | 4 | 5 | getResponseBuffer KEYWORD2 6 | clearResponseBuffer KEYWORD2 7 | setTransmitBuffer KEYWORD2 8 | clearTransmitBuffer KEYWORD2 9 | setSlaveAddress KEYWORD2 10 | send KEYWORD2 11 | available KEYWORD2 12 | receive KEYWORD2 13 | readCoils KEYWORD2 14 | readDiscreteInputs KEYWORD2 15 | readHoldingRegisters KEYWORD2 16 | readInputRegisters KEYWORD2 17 | writeSingleCoil KEYWORD2 18 | writeSingleRegister KEYWORD2 19 | writeMultipleCoils KEYWORD2 20 | writeMultipleCoils KEYWORD2 21 | writeMultipleRegisters KEYWORD2 22 | writeMultipleRegisters KEYWORD2 23 | maskWriteRegister KEYWORD2 24 | readWriteMultipleRegisters KEYWORD2 25 | readWriteMultipleRegisters KEYWORD2 26 | 27 | -------------------------------------------------------------------------------- /ModBusMaster232/keywords.txt~: -------------------------------------------------------------------------------- 1 | ModbusMaster232 KEYWORD1 2 | node KEYWORD1 3 | 4 | 5 | getResponseBuffer KEYWORD2 6 | clearResponseBuffer KEYWORD2 7 | setTransmitBuffer KEYWORD2 8 | clearTransmitBuffer KEYWORD2 9 | setSlaveAddress KEYWORD2 10 | send KEYWORD2 11 | available KEYWORD2 12 | receive KEYWORD2 13 | readCoils KEYWORD2 14 | readDiscreteInputs KEYWORD2 15 | readHoldingRegisters KEYWORD2 16 | readInputRegisters KEYWORD2 17 | writeSingleCoil KEYWORD2 18 | writeSingleRegister KEYWORD2 19 | writeMultipleCoils KEYWORD2 20 | writeMultipleCoils KEYWORD2 21 | writeMultipleRegisters KEYWORD2 22 | writeMultipleRegisters KEYWORD2 23 | maskWriteRegister KEYWORD2 24 | readWriteMultipleRegisters KEYWORD2 25 | readWriteMultipleRegisters KEYWORD2 26 | 27 | -------------------------------------------------------------------------------- /SoftwareSerial/SoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. 4 | Copyright (c) 2015-2016 Peter Lerup. All rights reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #include 23 | 24 | // The Arduino standard GPIO routines are not enough, 25 | // must use some from the Espressif SDK as well 26 | extern "C" { 27 | #include "gpio.h" 28 | } 29 | 30 | #include 31 | 32 | #define MAX_PIN 15 33 | 34 | // As the Arduino attachInterrupt has no parameter, lists of objects 35 | // and callbacks corresponding to each possible GPIO pins have to be defined 36 | SoftwareSerial *ObjList[MAX_PIN+1]; 37 | 38 | void ICACHE_RAM_ATTR sws_isr_0() { ObjList[0]->rxRead(); }; 39 | void ICACHE_RAM_ATTR sws_isr_1() { ObjList[1]->rxRead(); }; 40 | void ICACHE_RAM_ATTR sws_isr_2() { ObjList[2]->rxRead(); }; 41 | void ICACHE_RAM_ATTR sws_isr_3() { ObjList[3]->rxRead(); }; 42 | void ICACHE_RAM_ATTR sws_isr_4() { ObjList[4]->rxRead(); }; 43 | void ICACHE_RAM_ATTR sws_isr_5() { ObjList[5]->rxRead(); }; 44 | // Pin 6 to 11 can not be used 45 | void ICACHE_RAM_ATTR sws_isr_12() { ObjList[12]->rxRead(); }; 46 | void ICACHE_RAM_ATTR sws_isr_13() { ObjList[13]->rxRead(); }; 47 | void ICACHE_RAM_ATTR sws_isr_14() { ObjList[14]->rxRead(); }; 48 | void ICACHE_RAM_ATTR sws_isr_15() { ObjList[15]->rxRead(); }; 49 | 50 | static void (*ISRList[MAX_PIN+1])() = { 51 | sws_isr_0, 52 | sws_isr_1, 53 | sws_isr_2, 54 | sws_isr_3, 55 | sws_isr_4, 56 | sws_isr_5, 57 | NULL, 58 | NULL, 59 | NULL, 60 | NULL, 61 | NULL, 62 | NULL, 63 | sws_isr_12, 64 | sws_isr_13, 65 | sws_isr_14, 66 | sws_isr_15 67 | }; 68 | 69 | SoftwareSerial::SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic, unsigned int buffSize) { 70 | m_rxValid = m_txValid = m_txEnableValid = false; 71 | m_buffer = NULL; 72 | m_invert = inverse_logic; 73 | m_overflow = false; 74 | m_rxEnabled = false; 75 | if (isValidGPIOpin(receivePin)) { 76 | m_rxPin = receivePin; 77 | m_buffSize = buffSize; 78 | m_buffer = (uint8_t*)malloc(m_buffSize); 79 | if (m_buffer != NULL) { 80 | m_rxValid = true; 81 | m_inPos = m_outPos = 0; 82 | pinMode(m_rxPin, INPUT); 83 | ObjList[m_rxPin] = this; 84 | enableRx(true); 85 | } 86 | } 87 | if (isValidGPIOpin(transmitPin)) { 88 | m_txValid = true; 89 | m_txPin = transmitPin; 90 | pinMode(m_txPin, OUTPUT); 91 | digitalWrite(m_txPin, !m_invert); 92 | } 93 | // Default speed 94 | begin(9600); 95 | } 96 | 97 | SoftwareSerial::~SoftwareSerial() { 98 | enableRx(false); 99 | if (m_rxValid) 100 | ObjList[m_rxPin] = NULL; 101 | if (m_buffer) 102 | free(m_buffer); 103 | } 104 | 105 | bool SoftwareSerial::isValidGPIOpin(int pin) { 106 | return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= MAX_PIN); 107 | } 108 | 109 | void SoftwareSerial::begin(long speed) { 110 | // Use getCycleCount() loop to get as exact timing as possible 111 | m_bitTime = ESP.getCpuFreqMHz()*1000000/speed; 112 | } 113 | 114 | long SoftwareSerial::baudRate() { 115 | return ESP.getCpuFreqMHz()*1000000/m_bitTime; 116 | } 117 | 118 | void SoftwareSerial::setTransmitEnablePin(int transmitEnablePin) { 119 | if (isValidGPIOpin(transmitEnablePin)) { 120 | m_txEnableValid = true; 121 | m_txEnablePin = transmitEnablePin; 122 | pinMode(m_txEnablePin, OUTPUT); 123 | digitalWrite(m_txEnablePin, LOW); 124 | } else { 125 | m_txEnableValid = false; 126 | } 127 | } 128 | 129 | void SoftwareSerial::enableRx(bool on) { 130 | if (m_rxValid) { 131 | if (on) 132 | attachInterrupt(m_rxPin, ISRList[m_rxPin], m_invert ? RISING : FALLING); 133 | else 134 | detachInterrupt(m_rxPin); 135 | m_rxEnabled = on; 136 | } 137 | } 138 | 139 | int SoftwareSerial::read() { 140 | if (!m_rxValid || (m_inPos == m_outPos)) return -1; 141 | uint8_t ch = m_buffer[m_outPos]; 142 | m_outPos = (m_outPos+1) % m_buffSize; 143 | return ch; 144 | } 145 | 146 | int SoftwareSerial::available() { 147 | if (!m_rxValid) return 0; 148 | int avail = m_inPos - m_outPos; 149 | if (avail < 0) avail += m_buffSize; 150 | return avail; 151 | } 152 | 153 | #define WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bitTime; } 154 | 155 | size_t SoftwareSerial::write(uint8_t b) { 156 | if (!m_txValid) return 0; 157 | 158 | if (m_invert) b = ~b; 159 | // Disable interrupts in order to get a clean transmit 160 | cli(); 161 | if (m_txEnableValid) digitalWrite(m_txEnablePin, HIGH); 162 | unsigned long wait = m_bitTime; 163 | digitalWrite(m_txPin, HIGH); 164 | unsigned long start = ESP.getCycleCount(); 165 | // Start bit; 166 | digitalWrite(m_txPin, LOW); 167 | WAIT; 168 | for (int i = 0; i < 8; i++) { 169 | digitalWrite(m_txPin, (b & 1) ? HIGH : LOW); 170 | WAIT; 171 | b >>= 1; 172 | } 173 | // Stop bit 174 | digitalWrite(m_txPin, HIGH); 175 | WAIT; 176 | if (m_txEnableValid) digitalWrite(m_txEnablePin, LOW); 177 | sei(); 178 | return 1; 179 | } 180 | 181 | void SoftwareSerial::flush() { 182 | m_inPos = m_outPos = 0; 183 | } 184 | 185 | bool SoftwareSerial::overflow() { 186 | bool res = m_overflow; 187 | m_overflow = false; 188 | return res; 189 | } 190 | 191 | int SoftwareSerial::peek() { 192 | if (!m_rxValid || (m_inPos == m_outPos)) return -1; 193 | return m_buffer[m_outPos]; 194 | } 195 | 196 | void ICACHE_RAM_ATTR SoftwareSerial::rxRead() { 197 | // Advance the starting point for the samples but compensate for the 198 | // initial delay which occurs before the interrupt is delivered 199 | unsigned long wait = m_bitTime + m_bitTime/3 - 500; 200 | unsigned long start = ESP.getCycleCount(); 201 | uint8_t rec = 0; 202 | for (int i = 0; i < 8; i++) { 203 | WAIT; 204 | rec >>= 1; 205 | if (digitalRead(m_rxPin)) 206 | rec |= 0x80; 207 | } 208 | if (m_invert) rec = ~rec; 209 | // Stop bit 210 | WAIT; 211 | // Store the received value in the buffer unless we have an overflow 212 | int next = (m_inPos+1) % m_buffSize; 213 | if (next != m_outPos) { 214 | m_buffer[m_inPos] = rec; 215 | m_inPos = next; 216 | } else { 217 | m_overflow = true; 218 | } 219 | // Must clear this bit in the interrupt register, 220 | // it gets set even when interrupts are disabled 221 | GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rxPin); 222 | } 223 | -------------------------------------------------------------------------------- /SoftwareSerial/SoftwareSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | SoftwareSerial.h 3 | 4 | SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. 5 | Copyright (c) 2015-2016 Peter Lerup. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifndef SoftwareSerial_h 24 | #define SoftwareSerial_h 25 | 26 | #include 27 | #include 28 | 29 | 30 | // This class is compatible with the corresponding AVR one, 31 | // the constructor however has an optional rx buffer size. 32 | // Speed up to 115200 can be used. 33 | 34 | 35 | class SoftwareSerial : public Stream 36 | { 37 | public: 38 | SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64); 39 | ~SoftwareSerial(); 40 | 41 | void begin(long speed); 42 | long baudRate(); 43 | void setTransmitEnablePin(int transmitEnablePin); 44 | 45 | bool overflow(); 46 | int peek(); 47 | 48 | virtual size_t write(uint8_t byte); 49 | virtual int read(); 50 | virtual int available(); 51 | virtual void flush(); 52 | operator bool() {return m_rxValid || m_txValid;} 53 | 54 | // Disable or enable interrupts on the rx pin 55 | void enableRx(bool on); 56 | 57 | void rxRead(); 58 | 59 | // AVR compatibility methods 60 | bool listen() { enableRx(true); return true; } 61 | void end() { stopListening(); } 62 | bool isListening() { return m_rxEnabled; } 63 | bool stopListening() { enableRx(false); return true; } 64 | 65 | using Print::write; 66 | 67 | private: 68 | bool isValidGPIOpin(int pin); 69 | 70 | // Member variables 71 | int m_rxPin, m_txPin, m_txEnablePin; 72 | bool m_rxValid, m_rxEnabled; 73 | bool m_txValid, m_txEnableValid; 74 | bool m_invert; 75 | bool m_overflow; 76 | unsigned long m_bitTime; 77 | unsigned int m_inPos, m_outPos; 78 | int m_buffSize; 79 | uint8_t *m_buffer; 80 | 81 | }; 82 | 83 | // If only one tx or rx wanted then use this as parameter for the unused pin 84 | #define SW_SERIAL_UNUSED_PIN -1 85 | 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Software serial multple serial test 3 | 4 | Receives from the hardware serial, sends to software serial. 5 | Receives from software serial, sends to hardware serial. 6 | 7 | The circuit: 8 | * RX is digital pin 10 (connect to TX of other device) 9 | * TX is digital pin 11 (connect to RX of other device) 10 | 11 | Note: 12 | Not all pins on the Mega and Mega 2560 support change interrupts, 13 | so only the following can be used for RX: 14 | 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69 15 | 16 | Not all pins on the Leonardo support change interrupts, 17 | so only the following can be used for RX: 18 | 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI). 19 | 20 | created back in the mists of time 21 | modified 25 May 2012 22 | by Tom Igoe 23 | based on Mikal Hart's example 24 | 25 | This example code is in the public domain. 26 | 27 | */ 28 | #include 29 | 30 | SoftwareSerial mySerial(10, 11); // RX, TX 31 | 32 | void setup() 33 | { 34 | // Open serial communications and wait for port to open: 35 | Serial.begin(57600); 36 | while (!Serial) { 37 | ; // wait for serial port to connect. Needed for Leonardo only 38 | } 39 | 40 | 41 | Serial.println("Goodnight moon!"); 42 | 43 | // set the data rate for the SoftwareSerial port 44 | mySerial.begin(4800); 45 | mySerial.println("Hello, world?"); 46 | } 47 | 48 | void loop() // run over and over 49 | { 50 | if (mySerial.available()) 51 | Serial.write(mySerial.read()); 52 | if (Serial.available()) 53 | mySerial.write(Serial.read()); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /SoftwareSerial/examples/TwoPortReceive/TwoPortReceive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Software serial multple serial test 3 | 4 | Receives from the two software serial ports, 5 | sends to the hardware serial port. 6 | 7 | In order to listen on a software port, you call port.listen(). 8 | When using two software serial ports, you have to switch ports 9 | by listen()ing on each one in turn. Pick a logical time to switch 10 | ports, like the end of an expected transmission, or when the 11 | buffer is empty. This example switches ports when there is nothing 12 | more to read from a port 13 | 14 | The circuit: 15 | Two devices which communicate serially are needed. 16 | * First serial device's TX attached to digital pin 2, RX to pin 3 17 | * Second serial device's TX attached to digital pin 4, RX to pin 5 18 | 19 | Note: 20 | Not all pins on the Mega and Mega 2560 support change interrupts, 21 | so only the following can be used for RX: 22 | 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69 23 | 24 | Not all pins on the Leonardo support change interrupts, 25 | so only the following can be used for RX: 26 | 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI). 27 | 28 | created 18 Apr. 2011 29 | modified 25 May 2012 30 | by Tom Igoe 31 | based on Mikal Hart's twoPortRXExample 32 | 33 | This example code is in the public domain. 34 | 35 | */ 36 | 37 | #include 38 | // software serial #1: TX = digital pin 10, RX = digital pin 11 39 | SoftwareSerial portOne(10,11); 40 | 41 | // software serial #2: TX = digital pin 8, RX = digital pin 9 42 | // on the Mega, use other pins instead, since 8 and 9 don't work on the Mega 43 | SoftwareSerial portTwo(8,9); 44 | 45 | void setup() 46 | { 47 | // Open serial communications and wait for port to open: 48 | Serial.begin(9600); 49 | while (!Serial) { 50 | ; // wait for serial port to connect. Needed for Leonardo only 51 | } 52 | 53 | 54 | // Start each software serial port 55 | portOne.begin(9600); 56 | portTwo.begin(9600); 57 | } 58 | 59 | void loop() 60 | { 61 | // By default, the last intialized port is listening. 62 | // when you want to listen on a port, explicitly select it: 63 | portOne.listen(); 64 | Serial.println("Data from port one:"); 65 | // while there is data coming in, read it 66 | // and send to the hardware serial port: 67 | while (portOne.available() > 0) { 68 | char inByte = portOne.read(); 69 | Serial.write(inByte); 70 | } 71 | 72 | // blank line to separate data from the two ports: 73 | Serial.println(); 74 | 75 | // Now listen on the second port 76 | portTwo.listen(); 77 | // while there is data coming in, read it 78 | // and send to the hardware serial port: 79 | Serial.println("Data from port two:"); 80 | while (portTwo.available() > 0) { 81 | char inByte = portTwo.read(); 82 | Serial.write(inByte); 83 | } 84 | 85 | // blank line to separate data from the two ports: 86 | Serial.println(); 87 | } 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /SoftwareSerial/examples/swsertest/swsertest.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | SoftwareSerial swSer(14, 12, false, 256); 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | swSer.begin(115200); 9 | 10 | Serial.println("\nSoftware serial test started"); 11 | 12 | for (char ch = ' '; ch <= 'z'; ch++) { 13 | swSer.write(ch); 14 | } 15 | swSer.println(""); 16 | 17 | } 18 | 19 | void loop() { 20 | while (swSer.available() > 0) { 21 | Serial.write(swSer.read()); 22 | } 23 | while (Serial.available() > 0) { 24 | swSer.write(Serial.read()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /SoftwareSerial/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for NewSoftSerial 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | NewSoftSerial KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | end KEYWORD2 17 | read KEYWORD2 18 | available KEYWORD2 19 | isListening KEYWORD2 20 | overflow KEYWORD2 21 | flush KEYWORD2 22 | listen KEYWORD2 23 | 24 | ####################################### 25 | # Constants (LITERAL1) 26 | ####################################### 27 | 28 | --------------------------------------------------------------------------------