├── .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 |
--------------------------------------------------------------------------------