├── .gitignore ├── LICENSE ├── README.md ├── examples └── simple │ └── simple.ino ├── extras └── README ├── keywords.txt ├── library.properties └── src ├── ModbusSlaveTCP.cpp └── ModbusSlaveTCP.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModbusSlaveTCP 2 | 3 | ##### ModbusSlave library for ESP8266-Arduino 4 | 5 | This modbus slave library uses callbacks to handle modbus requests. 6 | Handler functions are called on modbus request, and the users can implement them in their sketch. 7 | 8 | ### ModbusSlave is fun and easy to use 9 | Register a handler function: 10 | ```c 11 | slave.cbVector[CB_READ_REGISTERS] = ReadAnalogIn; 12 | ``` 13 | Implement it: 14 | ```c 15 | void ReadAnalogIn(uint8_t fc, uint16_t address, uint16_t length) { 16 | for (int i = 0; i < length; i++) 17 | slave.writeRegisterToBuffer(i, analogRead(address + i)); 18 | } 19 | ``` 20 | And thats it, your sketch is modbus enabled. (see the full examples for more detail) 21 | 22 | ---- 23 | 24 | - [Install](#install) 25 | - [Competabilty](#competabilty) 26 | - [Callback vector](#callback-vector) 27 | - [Slots](#slots) 28 | - [Handler function](#handler-function) 29 | - [Function codes](#function-codes) 30 | - [Reading and writing to the request buffer](#reading-and-writing-to-the-request-buffer) 31 | - [Examples](#examples) 32 | - [handle "Force Single Coil" as arduino digitalWrite](#handle-force-single-coil-as-arduino-digitalwrite) 33 | - [handle "Read Input Registers" as arduino analogRead](#handle-read-input-registers-as-arduino-analogread) 34 | 35 | ---- 36 | 37 | ### Install 38 | 39 | Download the zip package, and install it into your Arduino IDE. 40 | 41 | See the Arduino tutorial about installing 3rd party libraries: https://www.arduino.cc/en/Guide/Libraries#toc4 42 | 43 | ### Competabilty 44 | 45 | ###### This class implements: 46 | 47 | * FC1 "Read Coil Status" 48 | * FC2 "Read Input Status" 49 | * FC3 "Read Holding Registers" 50 | * FC4 "Read Input Registers" 51 | * FC5 "Force Single Coil" 52 | * FC16 "Preset Multiple Registers" 53 | 54 | ### Callback vector 55 | 56 | Users register handler functions into the callback vector. 57 | 58 | ###### Slots 59 | 60 | The callback vector has 4 slots for request handlers: 61 | 62 | * slave.cbVector[CB_READ_COILS] - called on FC1 and FC2 63 | * slave.cbVector[CB_WRITE_COIL] - called on FC5 64 | * slave.cbVector[CB_READ_REGISTERS] - called on FC3 and FC4 65 | * slave.cbVector[CB_WRITE_MULTIPLE_REGISTERS] - called on FC16 66 | 67 | ###### Handler function 68 | 69 | handler functions must return void and take: 70 | * uint8_t fc - request function code 71 | * uint16_t address - first register / first coil address 72 | * uint16_t length / status - length of data / coil status 73 | 74 | ###### Function codes 75 | 76 | * FC_READ_COILS = 1 77 | * FC_READ_DISCRETE_INPUT = 2 78 | * FC_READ_REGISTERS = 3 79 | * FC_READ_INPUT_REGISTERS = 4 80 | * FC_WRITE_COIL = 5 81 | * FC_WRITE_MULTIPLE_REGISTERS = 16 82 | 83 | ---- 84 | 85 | ###### Reading and writing to the request / response buffer 86 | 87 | * uint16_t readRegisterFromBuffer(int offset) : read one register value from the request buffer. 88 | * void writeCoilToBuffer(int offset, uint16_t state) : write one coil state into the answer buffer. 89 | * void writeRegisterToBuffer(int offset, uint16_t value) : write one register value into the answer buffer. 90 | 91 | ---- 92 | 93 | ### Examples 94 | 95 | ---- 96 | ###### handle "Force Single Coil" as arduino digitalWrite 97 | ```c 98 | #include 99 | #include 100 | 101 | const char* ssid = "...."; 102 | const char* pass = "...."; 103 | 104 | Modbus slave(1); // slave id = 1 105 | 106 | void setup() { 107 | // connect to WiFi 108 | WiFi.begin(ssid, pass); 109 | while (WiFi.status() != WL_CONNECTED) delay(500); 110 | 111 | // register one handler functions 112 | // if a callback handler is not assigned to a modbus command 113 | // the default handler is called. 114 | // default handlers return a valid but empty replay. 115 | slave.cbVector[CB_WRITE_COIL] = writeDigitlOut; 116 | 117 | // start slave at port 502 118 | slave.begin(); // start listen on port 502 119 | } 120 | 121 | void loop() { 122 | // listen for modbus commands con serial port 123 | slave.poll(); 124 | } 125 | 126 | // Handel Force Single Coil (FC=05) 127 | void writeDigitlOut(uint8_t fc, uint16_t address, uint16_t status) { 128 | if (status == HIGH) { 129 | digitalWrite(address, HIGH); 130 | } else { 131 | digitalWrite(address, LOW); 132 | } 133 | } 134 | 135 | ``` 136 | 137 | ---- 138 | ###### handle "Read Input Registers" as arduino analogRead 139 | ```c 140 | #include 141 | #include 142 | 143 | const char* ssid = "...."; 144 | const char* pass = "...."; 145 | 146 | Modbus slave(1); // slave id = 1 147 | 148 | void setup() { 149 | // connect to WiFi 150 | WiFi.begin(ssid, pass); 151 | while (WiFi.status() != WL_CONNECTED) delay(500); 152 | 153 | // register handler functions 154 | slave.cbVector[CB_READ_REGISTERS] = ReadAnalogIn; 155 | 156 | // start slave at port 502 157 | slave.begin(); // start listen on port 502 158 | } 159 | 160 | void loop() { 161 | // listen for modbus commands con serial port 162 | slave.poll(); 163 | } 164 | 165 | // Handel Read Input Registers (FC=04) 166 | void ReadAnalogIn(uint8_t fc, uint16_t address, uint16_t length) { 167 | // we only answer to function code 4 168 | if (fc != FC_READ_INPUT_REGISTERS) return; 169 | 170 | // write registers into the answer buffer 171 | for (int i = 0; i < length; i++) { 172 | slave.writeRegisterToBuffer(i, analogRead(address + i)); 173 | } 174 | } 175 | 176 | ``` 177 | 178 | -------------------------------------------------------------------------------- /examples/simple/simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus slave simple example 3 | 4 | Control and Read Arduino I/Os using Modbus serial connection. 5 | 6 | This sketch show how to use the callback vector for reading and 7 | controleing Arduino I/Os. 8 | 9 | * Controls digital output pins as modbus coils. 10 | * Reads digital inputs state as discreet inputs. 11 | * Reads analog inputs as input registers. 12 | 13 | The circuit: ( see: ./extras/ModbusSetch.pdf ) 14 | * An ESP8266 unit with connectors to digital out pins. 15 | * 2 x LEDs, with 220 ohm resistors in series. 16 | * A switch connected to a digital input pin. 17 | * A potentiometer connected to an analog input pin. 18 | 19 | Created 8 12 2015 20 | By Yaacov Zamir 21 | 22 | https://github.com/yaacov/ArduinoModbusSlave 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | const char* ssid = "..."; 30 | const char* pass = "..."; 31 | 32 | /* slave id = 1, rs485 control-pin = 8, baud = 9600 33 | */ 34 | #define SLAVE_ID 1 35 | 36 | /** 37 | * Modbus object declaration 38 | */ 39 | ModbusTCP slave(SLAVE_ID); 40 | 41 | void setup() { 42 | /* Start serial port 43 | */ 44 | Serial.begin(115200); 45 | 46 | /* Connect WiFi to the network 47 | */ 48 | Serial.print("Connecting to "); 49 | Serial.println(ssid); 50 | WiFi.begin(ssid, pass); 51 | 52 | while (WiFi.status() != WL_CONNECTED) { 53 | delay(500); 54 | Serial.print("."); 55 | } 56 | 57 | /* register handler functions 58 | * into the modbus slave callback vector. 59 | */ 60 | slave.cbVector[CB_WRITE_COIL] = writeDigitlOut; 61 | slave.cbVector[CB_READ_COILS] = readDigitalIn; 62 | slave.cbVector[CB_READ_REGISTERS] = readAnalogIn; 63 | 64 | /* start slave and listen to TCP port 502 65 | */ 66 | slave.begin(); 67 | 68 | // log to serial port 69 | Serial.println(""); 70 | Serial.print("Modbus ready, listen on "); 71 | Serial.print(WiFi.localIP()); 72 | Serial.println(" : 502"); 73 | } 74 | 75 | void loop() { 76 | /* listen for modbus commands con serial port 77 | * 78 | * on a request, handle the request. 79 | * if the request has a user handler function registered in cbVector 80 | * call the user handler function. 81 | */ 82 | slave.poll(); 83 | } 84 | 85 | /** 86 | * Handel Force Single Coil (FC=05) 87 | * set digital output pins (coils) on and off 88 | */ 89 | void writeDigitlOut(uint8_t fc, uint16_t address, uint16_t status) { 90 | digitalWrite(address, status); 91 | } 92 | 93 | /** 94 | * Handel Read Input Status (FC=02/01) 95 | * write back the values from digital in pins (input status). 96 | * 97 | * handler functions must return void and take: 98 | * uint8_t fc - function code 99 | * uint16_t address - first register/coil address 100 | * uint16_t length/status - length of data / coil status 101 | */ 102 | void readDigitalIn(uint8_t fc, uint16_t address, uint16_t length) { 103 | // read digital input 104 | for (int i = 0; i < length; i++) { 105 | slave.writeCoilToBuffer(i, digitalRead(address + i)); 106 | } 107 | } 108 | 109 | /** 110 | * Handel Read Input Registers (FC=04/03) 111 | * write back the values from analog in pins (input registers). 112 | */ 113 | void readAnalogIn(uint8_t fc, uint16_t address, uint16_t length) { 114 | // read analog input 115 | for (int i = 0; i < length; i++) { 116 | slave.writeRegisterToBuffer(i, analogRead(address + i)); 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /extras/README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaacov/ArduinoModbusSlaveTCP/1860997c63c2be2c8a2c19ff5b45f0c64cc4baca/extras/README -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For ModbusSlave 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | ModbusTCP KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | begin KEYWORD2 14 | poll KEYWORD2 15 | readRegisterFromBuffer KEYWORD2 16 | writeCoilToBuffer KEYWORD2 17 | writeRegisterToBuffer KEYWORD2 18 | writeStringToBuffer KEYWORD2 19 | 20 | ####################################### 21 | # Instances (KEYWORD2) 22 | ####################################### 23 | cbVector KEYWORD2 24 | 25 | ####################################### 26 | # Constants (LITERAL1) 27 | ####################################### 28 | MAX_BUFFER LITERAL1 29 | FC_READ_COILS LITERAL1 30 | FC_READ_DISCRETE_INPUT LITERAL1 31 | FC_READ_HOLDING_REGISTERS LITERAL1 32 | FC_READ_INPUT_REGISTERS LITERAL1 33 | FC_WRITE_COIL LITERAL1 34 | FC_WRITE_MULTIPLE_REGISTERS LITERAL1 35 | CB_READ_COILS LITERAL1 36 | CB_READ_REGISTERS LITERAL1 37 | CB_WRITE_COIL LITERAL1 38 | CB_WRITE_MULTIPLE_REGISTERS LITERAL1 39 | COIL_OFF LITERAL1 40 | COIL_ON LITERAL1 41 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ModbusSlaveTCP 2 | version=1.0.2 3 | author=Yaacov Zamir 4 | maintainer=Yaacov Zamir 5 | sentence=A Modbus TCP Slave library for ESP8266-Arduino. 6 | paragraph=This modbus slave library uses callbacks to handle modbus requests. 7 | category=Communication 8 | url=https://github.com/yaacov/ArduinoModbusSlaveTCP 9 | architectures=esp8266 10 | -------------------------------------------------------------------------------- /src/ModbusSlaveTCP.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Yaacov Zamir 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "Arduino.h" 20 | #include "Print.h" 21 | 22 | #include 23 | #include "ModbusSlaveTCP.h" 24 | 25 | #define MLEN 6 26 | 27 | /** 28 | * Create a server that listen to port 502 29 | */ 30 | WiFiServer server(502); 31 | WiFiClient client; 32 | 33 | /** 34 | * Init the modbus object. 35 | * 36 | * @param unitID the modbus slave id. 37 | */ 38 | ModbusTCP::ModbusTCP(uint8_t _unitID) { 39 | // set modbus slave unit id 40 | unitID = _unitID; 41 | } 42 | 43 | /** 44 | * Begin server. 45 | */ 46 | void ModbusTCP::begin() { 47 | server.begin(); 48 | } 49 | 50 | /** 51 | * wait for end of frame, parse request and answer it. 52 | */ 53 | int ModbusTCP::poll() { 54 | size_t lengthIn; 55 | size_t lengthOut; 56 | uint16_t crc; 57 | uint16_t address; 58 | uint16_t length; 59 | uint16_t status; 60 | uint8_t fc; 61 | 62 | /** 63 | * Check if there is new clients 64 | */ 65 | if (server.hasClient()){ 66 | // if client is free - connect 67 | if (!client || !client.connected()){ 68 | if(client) client.stop(); 69 | client = server.available(); 70 | 71 | // client is not free - reject 72 | } else { 73 | WiFiClient serverClient = server.available(); 74 | serverClient.stop(); 75 | } 76 | } 77 | 78 | /** 79 | * Read one data frame: 80 | */ 81 | 82 | // if we have data in buffer 83 | // read until end of data. 84 | if (client && client.connected() && client.available()) { 85 | lengthIn = 0; 86 | 87 | //get data from the telnet client and push it to the UART 88 | while(client.available() && lengthIn < MAX_BUFFER) { 89 | bufIn[lengthIn] = client.read(); 90 | lengthIn++; 91 | } 92 | } else { 93 | return 0; 94 | } 95 | 96 | /** 97 | * Validate buffer. 98 | */ 99 | // check minimum length. 100 | if (lengthIn < (MLEN + 6)) return 0; 101 | 102 | // check unit-id 103 | if (bufIn[MLEN + 0] != unitID) return 0; 104 | 105 | /** 106 | * Parse command 107 | */ 108 | fc = bufIn[MLEN + 1]; 109 | switch (fc) { 110 | case FC_READ_COILS: // read coils (digital out state) 111 | case FC_READ_DISCRETE_INPUT: // read input state (digital in) 112 | address = word(bufIn[MLEN + 2], bufIn[MLEN + 3]); // coil to set. 113 | length = word(bufIn[MLEN + 4], bufIn[MLEN + 5]); 114 | 115 | // sanity check. 116 | if (length > MAX_BUFFER) return 0; 117 | 118 | // check command length. 119 | if (lengthIn != (MLEN + 6)) return 0; 120 | 121 | // build valid empty answer. 122 | lengthOut = MLEN + 3 + (length - 1) / 8 + 1; 123 | bufOut[MLEN + 2] = (length - 1) / 8 + 1; 124 | 125 | // clear data out. 126 | memset(MLEN + bufOut + 3, 0, bufOut[2]); 127 | 128 | // if we have uset callback. 129 | if (cbVector[CB_READ_COILS]) { 130 | cbVector[CB_READ_COILS](fc, address, length); 131 | } 132 | break; 133 | case FC_READ_HOLDING_REGISTERS: // read holding registers (analog out state) 134 | case FC_READ_INPUT_REGISTERS: // read input registers (analog in) 135 | address = word(bufIn[MLEN + 2], bufIn[MLEN + 3]); // first register. 136 | length = word(bufIn[MLEN + 4], bufIn[MLEN + 5]); // number of registers to read. 137 | 138 | // sanity check. 139 | if (length > MAX_BUFFER) return 0; 140 | 141 | // check command length. 142 | if (lengthIn != (MLEN + 6)) return 0; 143 | 144 | // build valid empty answer. 145 | lengthOut = MLEN + 3 + 2 * length; 146 | bufOut[MLEN + 2] = 2 * length; 147 | 148 | // clear data out. 149 | memset(MLEN + bufOut + MLEN + 3, 0, bufOut[2]); 150 | 151 | // if we have uset callback. 152 | if (cbVector[CB_READ_REGISTERS]) { 153 | cbVector[CB_READ_REGISTERS](fc, address, length); 154 | } 155 | break; 156 | case FC_WRITE_COIL: // write one coil (digital out) 157 | address = word(bufIn[MLEN + 2], bufIn[MLEN + 3]); // coil to set 158 | status = word(bufIn[MLEN + 4], bufIn[MLEN + 5]); // 0xff00 - on, 0x0000 - off 159 | 160 | // check command length. 161 | if (lengthIn != (MLEN + 6)) return 0; 162 | 163 | // build valid empty answer. 164 | lengthOut = MLEN + 6; 165 | memcpy(MLEN + bufOut + 2, MLEN + bufIn + 2, 4); 166 | 167 | // if we have uset callback. 168 | if (cbVector[CB_WRITE_COIL]) { 169 | cbVector[CB_WRITE_COIL](fc, address, status == COIL_ON); 170 | } 171 | break; 172 | case FC_WRITE_MULTIPLE_REGISTERS: // write holding registers (analog out) 173 | address = word(bufIn[MLEN + 2], bufIn[MLEN + 3]); // first register 174 | length = word(bufIn[MLEN + 4], bufIn[MLEN + 5]); // number of registers to set 175 | 176 | // sanity check. 177 | if (length > MAX_BUFFER) return 0; 178 | 179 | // check command length 180 | if (lengthIn != (MLEN + 7 + length * 2)) return 0; 181 | 182 | // build valid empty answer. 183 | lengthOut = MLEN + 6; 184 | memcpy(MLEN + bufOut + 2, MLEN + bufIn + 2, 4); 185 | 186 | // if we have uset callback 187 | if (cbVector[CB_WRITE_MULTIPLE_REGISTERS]) { 188 | cbVector[CB_WRITE_MULTIPLE_REGISTERS](fc, address, length); 189 | } 190 | break; 191 | default: 192 | // unknown command 193 | return 0; 194 | break; 195 | } 196 | 197 | /** 198 | * Build answer 199 | */ 200 | bufOut[0] = bufIn[0]; // transaction Identifier msb 201 | bufOut[1] = bufIn[1]; // transaction Identifier lsb 202 | bufOut[2] = 0; // protocol msb 203 | bufOut[3] = 0; // protocol lsb 204 | bufOut[4] = 0; // msg length msb 205 | bufOut[5] = lengthOut - MLEN; // msg length lsb 206 | 207 | bufOut[MLEN + 0] = unitID; 208 | bufOut[MLEN + 1] = fc; 209 | 210 | /** 211 | * Transmit 212 | */ 213 | if (client && client.connected()) { 214 | client.write((uint8_t*)bufOut, lengthOut); 215 | delay(1); 216 | } 217 | 218 | return lengthOut; 219 | } 220 | 221 | /** 222 | * Read register value from input buffer. 223 | * 224 | * @param offset the register offset from first register in this buffer. 225 | * @return the reguster value from buffer. 226 | */ 227 | uint16_t ModbusTCP::readRegisterFromBuffer(int offset) { 228 | int address = MLEN + 7 + offset * 2; 229 | 230 | return word(bufIn[address], bufIn[address + 1]); 231 | } 232 | 233 | /** 234 | * Write coil state to output buffer. 235 | * 236 | * @param offset the coil offset from first coil in this buffer. 237 | * @param state the coil state to write into buffer (true / false) 238 | */ 239 | void ModbusTCP::writeCoilToBuffer(int offset, uint16_t state) { 240 | int address = MLEN + 3 + offset / 8; 241 | int bit = offset % 8; 242 | 243 | if (state == HIGH) { 244 | bitSet(bufOut[address], bit); 245 | } else { 246 | bitClear(bufOut[address], bit); 247 | } 248 | } 249 | 250 | /** 251 | * Write register value to output buffer. 252 | * 253 | * @param offset the register offset from first register in this buffer. 254 | * @param value the register value to write into buffer. 255 | */ 256 | void ModbusTCP::writeRegisterToBuffer(int offset, uint16_t value) { 257 | int address = MLEN + 3 + offset * 2; 258 | 259 | bufOut[address] = value >> 8; 260 | bufOut[address + 1] = value & 0xff; 261 | } 262 | 263 | /** 264 | * Write arbitrary string of uint8_t to output buffer. 265 | * 266 | * @param offset the register offset from first register in this buffer. 267 | * @param str the string to write into buffer. 268 | * @param length the string length. 269 | */ 270 | void ModbusTCP::writeStringToBuffer(int offset, uint8_t *str, uint8_t length) { 271 | int address = MLEN + 3 + offset * 2; 272 | 273 | // check string length. 274 | if ((address + length) >= MAX_BUFFER) return; 275 | 276 | memcpy(bufOut + address, str, length); 277 | } 278 | -------------------------------------------------------------------------------- /src/ModbusSlaveTCP.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Yaacov Zamir 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #define MAX_BUFFER 64 20 | 21 | /** 22 | * Modbus function codes 23 | */ 24 | enum { 25 | FC_READ_COILS = 1, 26 | FC_READ_DISCRETE_INPUT = 2, 27 | FC_READ_HOLDING_REGISTERS = 3, 28 | FC_READ_INPUT_REGISTERS = 4, 29 | FC_WRITE_COIL = 5, 30 | FC_WRITE_MULTIPLE_REGISTERS = 16 31 | }; 32 | 33 | enum { 34 | CB_READ_COILS = 0, 35 | CB_READ_REGISTERS, 36 | CB_WRITE_COIL, 37 | CB_WRITE_MULTIPLE_REGISTERS 38 | }; 39 | 40 | enum { 41 | COIL_OFF = 0x0000, 42 | COIL_ON = 0xff00 43 | }; 44 | 45 | typedef void(*CallBackFunc)(uint8_t, uint16_t, uint16_t); 46 | 47 | /** 48 | * @class Modbus 49 | */ 50 | class ModbusTCP { 51 | public: 52 | ModbusTCP(uint8_t unitID); 53 | void begin(); 54 | int poll(); 55 | uint16_t readRegisterFromBuffer(int offset); 56 | void writeCoilToBuffer(int offset, uint16_t state); 57 | void writeRegisterToBuffer(int offset, uint16_t value); 58 | void writeStringToBuffer(int offset, uint8_t *str, uint8_t length); 59 | 60 | CallBackFunc cbVector[4]; 61 | private: 62 | uint8_t unitID; 63 | uint8_t bufIn[MAX_BUFFER]; 64 | uint8_t bufOut[MAX_BUFFER]; 65 | }; 66 | 67 | --------------------------------------------------------------------------------