From ddcfc72a2e6c8c8cde4f2132b30735b193408391 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Mon, 17 Oct 2016 11:21:44 +0330 Subject: [PATCH 01/10] Multiple Coils and Input Registers Read/Write Bug fixed! --- libraries/Modbus/Modbus.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/Modbus/Modbus.cpp b/libraries/Modbus/Modbus.cpp index 6e9c7da..c5cdf15 100644 --- a/libraries/Modbus/Modbus.cpp +++ b/libraries/Modbus/Modbus.cpp @@ -1,6 +1,6 @@ /* Modbus.cpp - Source for Modbus Base Library - Copyright (C) 2014 André Sarmento Barbosa + Copyright (C) 2014 André Sarmento Barbosa */ #include "Modbus.h" @@ -326,7 +326,7 @@ void Modbus::readCoils(word startreg, word numregs) { byte bitn = 0; word totregs = numregs; word i; - while (numregs--) { + while (numregs) { i = (totregs - numregs) / 8; if (this->Coil(startreg)) bitSet(_frame[2+i], bitn); @@ -337,6 +337,7 @@ void Modbus::readCoils(word startreg, word numregs) { if (bitn == 8) bitn = 0; //increment the register startreg++; + numregs--; } _reply = MB_REPLY_NORMAL; @@ -377,7 +378,7 @@ void Modbus::readInputStatus(word startreg, word numregs) { byte bitn = 0; word totregs = numregs; word i; - while (numregs--) { + while (numregs) { i = (totregs - numregs) / 8; if (this->Ists(startreg)) bitSet(_frame[2+i], bitn); @@ -388,6 +389,7 @@ void Modbus::readInputStatus(word startreg, word numregs) { if (bitn == 8) bitn = 0; //increment the register startreg++; + numregs--; } _reply = MB_REPLY_NORMAL; @@ -496,7 +498,7 @@ void Modbus::writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte byte bitn = 0; word totoutputs = numoutputs; word i; - while (numoutputs--) { + while (numoutputs) { i = (totoutputs - numoutputs) / 8; this->Coil(startreg, bitRead(frame[6+i], bitn)); //increment the bit index @@ -504,6 +506,7 @@ void Modbus::writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte if (bitn == 8) bitn = 0; //increment the register startreg++; + numoutputs--; } _reply = MB_REPLY_NORMAL; From 9df0fd911737ff430be72703f72b1f6b2b803f77 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Mon, 17 Oct 2016 11:22:43 +0330 Subject: [PATCH 02/10] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 822f437..7c5c286 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Read/Write Multiple Coil/Inputs Fixed! (8 bit bug) Modbus Library for Arduino ========================== From d18fca8b5eb594a93a92197a389f1f82896626f0 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Thu, 5 Jan 2017 22:14:59 +0330 Subject: [PATCH 03/10] adding a flag and client holder for keeping track of client --- libraries/ModbusIP/ModbusIP.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/ModbusIP/ModbusIP.h b/libraries/ModbusIP/ModbusIP.h index 29882e5..caa99a2 100644 --- a/libraries/ModbusIP/ModbusIP.h +++ b/libraries/ModbusIP/ModbusIP.h @@ -17,7 +17,9 @@ class ModbusIP : public Modbus { private: + EthernetClient _client; EthernetServer _server; + bool _firstClient; byte _MBAP[7]; public: From 518128c67962d31885ac8ede8415598baa367054 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Thu, 5 Jan 2017 22:16:53 +0330 Subject: [PATCH 04/10] these changes make only one client available and closes previous connection --- libraries/ModbusIP/ModbusIP.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/ModbusIP/ModbusIP.cpp b/libraries/ModbusIP/ModbusIP.cpp index c2caffb..9a5b4b0 100644 --- a/libraries/ModbusIP/ModbusIP.cpp +++ b/libraries/ModbusIP/ModbusIP.cpp @@ -5,6 +5,7 @@ #include "ModbusIP.h" ModbusIP::ModbusIP():_server(MODBUSIP_PORT) { + _firstClient = false; } void ModbusIP::config(uint8_t *mac) { @@ -36,6 +37,7 @@ void ModbusIP::task() { EthernetClient client = _server.available(); if (client) { + _firstClient = true; if (client.connected()) { int i = 0; while (client.available()){ @@ -81,5 +83,13 @@ void ModbusIP::task() { free(_frame); _len = 0; } + if(client != _client) + { + if(_firstClient) + { + _client.stop(); + } + _client = client; + } } } From a3c607081c94689f53412a7b0eb5e1f762f91ba5 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Thu, 5 Jan 2017 22:20:08 +0330 Subject: [PATCH 05/10] Delete Modbus.h --- libraries/Modbus/Modbus.h | 100 -------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 libraries/Modbus/Modbus.h diff --git a/libraries/Modbus/Modbus.h b/libraries/Modbus/Modbus.h deleted file mode 100644 index a52c875..0000000 --- a/libraries/Modbus/Modbus.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - Modbus.h - Header for Modbus Base Library - Copyright (C) 2014 André Sarmento Barbosa -*/ -#include "Arduino.h" - -#ifndef MODBUS_H -#define MODBUS_H - -#define MAX_REGS 32 -#define MAX_FRAME 128 -//#define USE_HOLDING_REGISTERS_ONLY - -typedef unsigned int u_int; - -//Function Codes -enum { - MB_FC_READ_COILS = 0x01, // Read Coils (Output) Status 0xxxx - MB_FC_READ_INPUT_STAT = 0x02, // Read Input Status (Discrete Inputs) 1xxxx - MB_FC_READ_REGS = 0x03, // Read Holding Registers 4xxxx - MB_FC_READ_INPUT_REGS = 0x04, // Read Input Registers 3xxxx - MB_FC_WRITE_COIL = 0x05, // Write Single Coil (Output) 0xxxx - MB_FC_WRITE_REG = 0x06, // Preset Single Register 4xxxx - MB_FC_WRITE_COILS = 0x0F, // Write Multiple Coils (Outputs) 0xxxx - MB_FC_WRITE_REGS = 0x10, // Write block of contiguous registers 4xxxx -}; - -//Exception Codes -enum { - MB_EX_ILLEGAL_FUNCTION = 0x01, // Function Code not Supported - MB_EX_ILLEGAL_ADDRESS = 0x02, // Output Address not exists - MB_EX_ILLEGAL_VALUE = 0x03, // Output Value not in Range - MB_EX_SLAVE_FAILURE = 0x04, // Slave Deive Fails to process request -}; - -//Reply Types -enum { - MB_REPLY_OFF = 0x01, - MB_REPLY_ECHO = 0x02, - MB_REPLY_NORMAL = 0x03, -}; - -typedef struct TRegister { - word address; - word value; - struct TRegister* next; -} TRegister; - -class Modbus { - private: - TRegister *_regs_head; - TRegister *_regs_last; - - void readRegisters(word startreg, word numregs); - void writeSingleRegister(word reg, word value); - void writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount); - void exceptionResponse(byte fcode, byte excode); - #ifndef USE_HOLDING_REGISTERS_ONLY - void readCoils(word startreg, word numregs); - void readInputStatus(word startreg, word numregs); - void readInputRegisters(word startreg, word numregs); - void writeSingleCoil(word reg, word status); - void writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount); - #endif - - TRegister* searchRegister(word addr); - - void addReg(word address, word value = 0); - bool Reg(word address, word value); - word Reg(word address); - - protected: - byte *_frame; - byte _len; - byte _reply; - void receivePDU(byte* frame); - - public: - Modbus(); - - void addHreg(word offset, word value = 0); - bool Hreg(word offset, word value); - word Hreg(word offset); - - #ifndef USE_HOLDING_REGISTERS_ONLY - void addCoil(word offset, bool value = false); - void addIsts(word offset, bool value = false); - void addIreg(word offset, word value = 0); - - bool Coil(word offset, bool value); - bool Ists(word offset, bool value); - bool Ireg(word offset, word value); - - bool Coil(word offset); - bool Ists(word offset); - word Ireg(word offset); - #endif -}; - -#endif //MODBUS_H From 0c6281b99fd8219b3ebef8e561950fc5ee0e89cd Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Thu, 5 Jan 2017 22:20:27 +0330 Subject: [PATCH 06/10] Delete Modbus.cpp --- libraries/Modbus/Modbus.cpp | 517 ------------------------------------ 1 file changed, 517 deletions(-) delete mode 100644 libraries/Modbus/Modbus.cpp diff --git a/libraries/Modbus/Modbus.cpp b/libraries/Modbus/Modbus.cpp deleted file mode 100644 index c5cdf15..0000000 --- a/libraries/Modbus/Modbus.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/* - Modbus.cpp - Source for Modbus Base Library - Copyright (C) 2014 André Sarmento Barbosa -*/ -#include "Modbus.h" - -Modbus::Modbus() { - _regs_head = 0; - _regs_last = 0; -} - -TRegister* Modbus::searchRegister(word address) { - TRegister *reg = _regs_head; - //if there is no register configured, bail - if(reg == 0) return(0); - //scan through the linked list until the end of the list or the register is found. - //return the pointer. - do { - if (reg->address == address) return(reg); - reg = reg->next; - } while(reg); - return(0); -} - -void Modbus::addReg(word address, word value) { - TRegister *newreg; - - newreg = (TRegister *) malloc(sizeof(TRegister)); - newreg->address = address; - newreg->value = value; - newreg->next = 0; - - if(_regs_head == 0) { - _regs_head = newreg; - _regs_last = _regs_head; - } else { - //Assign the last register's next pointer to newreg. - _regs_last->next = newreg; - //then make temp the last register in the list. - _regs_last = newreg; - } -} - -bool Modbus::Reg(word address, word value) { - TRegister *reg; - //search for the register address - reg = this->searchRegister(address); - //if found then assign the register value to the new value. - if (reg) { - reg->value = value; - return true; - } else - return false; -} - -word Modbus::Reg(word address) { - TRegister *reg; - reg = this->searchRegister(address); - if(reg) - return(reg->value); - else - return(0); -} - -void Modbus::addHreg(word offset, word value) { - this->addReg(offset + 40001, value); -} - -bool Modbus::Hreg(word offset, word value) { - return Reg(offset + 40001, value); -} - -word Modbus::Hreg(word offset) { - return Reg(offset + 40001); -} - -#ifndef USE_HOLDING_REGISTERS_ONLY - void Modbus::addCoil(word offset, bool value) { - this->addReg(offset + 1, value?0xFF00:0x0000); - } - - void Modbus::addIsts(word offset, bool value) { - this->addReg(offset + 10001, value?0xFF00:0x0000); - } - - void Modbus::addIreg(word offset, word value) { - this->addReg(offset + 30001, value); - } - - bool Modbus::Coil(word offset, bool value) { - return Reg(offset + 1, value?0xFF00:0x0000); - } - - bool Modbus::Ists(word offset, bool value) { - return Reg(offset + 10001, value?0xFF00:0x0000); - } - - bool Modbus::Ireg(word offset, word value) { - return Reg(offset + 30001, value); - } - - bool Modbus::Coil(word offset) { - if (Reg(offset + 1) == 0xFF00) { - return true; - } else return false; - } - - bool Modbus::Ists(word offset) { - if (Reg(offset + 10001) == 0xFF00) { - return true; - } else return false; - } - - word Modbus::Ireg(word offset) { - return Reg(offset + 30001); - } -#endif - - -void Modbus::receivePDU(byte* frame) { - byte fcode = frame[0]; - word field1 = (word)frame[1] << 8 | (word)frame[2]; - word field2 = (word)frame[3] << 8 | (word)frame[4]; - - switch (fcode) { - - case MB_FC_WRITE_REG: - //field1 = reg, field2 = value - this->writeSingleRegister(field1, field2); - break; - - case MB_FC_READ_REGS: - //field1 = startreg, field2 = numregs - this->readRegisters(field1, field2); - break; - - case MB_FC_WRITE_REGS: - //field1 = startreg, field2 = status - this->writeMultipleRegisters(frame,field1, field2, frame[5]); - break; - - #ifndef USE_HOLDING_REGISTERS_ONLY - case MB_FC_READ_COILS: - //field1 = startreg, field2 = numregs - this->readCoils(field1, field2); - break; - - case MB_FC_READ_INPUT_STAT: - //field1 = startreg, field2 = numregs - this->readInputStatus(field1, field2); - break; - - case MB_FC_READ_INPUT_REGS: - //field1 = startreg, field2 = numregs - this->readInputRegisters(field1, field2); - break; - - case MB_FC_WRITE_COIL: - //field1 = reg, field2 = status - this->writeSingleCoil(field1, field2); - break; - - case MB_FC_WRITE_COILS: - //field1 = startreg, field2 = numoutputs - this->writeMultipleCoils(frame,field1, field2, frame[5]); - break; - - #endif - default: - this->exceptionResponse(fcode, MB_EX_ILLEGAL_FUNCTION); - } -} - -void Modbus::exceptionResponse(byte fcode, byte excode) { - //Clean frame buffer - free(_frame); - _len = 2; - _frame = (byte *) malloc(_len); - _frame[0] = fcode + 0x80; - _frame[1] = excode; - - _reply = MB_REPLY_NORMAL; -} - -void Modbus::readRegisters(word startreg, word numregs) { - //Check value (numregs) - if (numregs < 0x0001 || numregs > 0x007D) { - this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address - //*** See comments on readCoils method. - if (!this->searchRegister(startreg + 40001)) { - this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_ADDRESS); - return; - } - - - //Clean frame buffer - free(_frame); - _len = 0; - - //calculate the query reply message length - //for each register queried add 2 bytes - _len = 2 + numregs * 2; - - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_READ_REGS, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_READ_REGS; - _frame[1] = _len - 2; //byte count - - word val; - word i = 0; - while(numregs--) { - //retrieve the value from the register bank for the current register - val = this->Hreg(startreg + i); - //write the high byte of the register value - _frame[2 + i * 2] = val >> 8; - //write the low byte of the register value - _frame[3 + i * 2] = val & 0xFF; - i++; - } - - _reply = MB_REPLY_NORMAL; -} - -void Modbus::writeSingleRegister(word reg, word value) { - //No necessary verify illegal value (EX_ILLEGAL_VALUE) - because using word (0x0000 - 0x0FFFF) - //Check Address and execute (reg exists?) - if (!this->Hreg(reg, value)) { - this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_ILLEGAL_ADDRESS); - return; - } - - //Check for failure - if (this->Hreg(reg) != value) { - this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_SLAVE_FAILURE); - return; - } - - _reply = MB_REPLY_ECHO; -} - -void Modbus::writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount) { - //Check value - if (numoutputs < 0x0001 || numoutputs > 0x007B || bytecount != 2 * numoutputs) { - this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address (startreg...startreg + numregs) - for (int k = 0; k < numoutputs; k++) { - if (!this->searchRegister(startreg + 40001 + k)) { - this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_ADDRESS); - return; - } - } - - //Clean frame buffer - free(_frame); - _len = 5; - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_WRITE_REGS; - _frame[1] = startreg >> 8; - _frame[2] = startreg & 0x00FF; - _frame[3] = numoutputs >> 8; - _frame[4] = numoutputs & 0x00FF; - - word val; - word i = 0; - while(numoutputs--) { - val = (word)frame[6+i*2] << 8 | (word)frame[7+i*2]; - this->Hreg(startreg + i, val); - i++; - } - - _reply = MB_REPLY_NORMAL; -} - -#ifndef USE_HOLDING_REGISTERS_ONLY -void Modbus::readCoils(word startreg, word numregs) { - //Check value (numregs) - if (numregs < 0x0001 || numregs > 0x07D0) { - this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address - //Check only startreg. Is this correct? - //When I check all registers in range I got errors in ScadaBR - //I think that ScadaBR request more than one in the single request - //when you have more then one datapoint configured from same type. - if (!this->searchRegister(startreg + 1)) { - this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); - return; - } - - //Clean frame buffer - free(_frame); - _len = 0; - - //Determine the message length = function type, byte count and - //for each group of 8 registers the message length increases by 1 - _len = 2 + numregs/8; - if (numregs%8) _len++; //Add 1 to the message length for the partial byte. - - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_READ_COILS, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_READ_COILS; - _frame[1] = _len - 2; //byte count (_len - function code and byte count) - - byte bitn = 0; - word totregs = numregs; - word i; - while (numregs) { - i = (totregs - numregs) / 8; - if (this->Coil(startreg)) - bitSet(_frame[2+i], bitn); - else - bitClear(_frame[2+i], bitn); - //increment the bit index - bitn++; - if (bitn == 8) bitn = 0; - //increment the register - startreg++; - numregs--; - } - - _reply = MB_REPLY_NORMAL; -} - -void Modbus::readInputStatus(word startreg, word numregs) { - //Check value (numregs) - if (numregs < 0x0001 || numregs > 0x07D0) { - this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address - //*** See comments on readCoils method. - if (!this->searchRegister(startreg + 10001)) { - this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); - return; - } - - //Clean frame buffer - free(_frame); - _len = 0; - - //Determine the message length = function type, byte count and - //for each group of 8 registers the message length increases by 1 - _len = 2 + numregs/8; - if (numregs%8) _len++; //Add 1 to the message length for the partial byte. - - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_READ_INPUT_STAT; - _frame[1] = _len - 2; - - byte bitn = 0; - word totregs = numregs; - word i; - while (numregs) { - i = (totregs - numregs) / 8; - if (this->Ists(startreg)) - bitSet(_frame[2+i], bitn); - else - bitClear(_frame[2+i], bitn); - //increment the bit index - bitn++; - if (bitn == 8) bitn = 0; - //increment the register - startreg++; - numregs--; - } - - _reply = MB_REPLY_NORMAL; -} - -void Modbus::readInputRegisters(word startreg, word numregs) { - //Check value (numregs) - if (numregs < 0x0001 || numregs > 0x007D) { - this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address - //*** See comments on readCoils method. - if (!this->searchRegister(startreg + 30001)) { - this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); - return; - } - - //Clean frame buffer - free(_frame); - _len = 0; - - //calculate the query reply message length - //for each register queried add 2 bytes - _len = 2 + numregs * 2; - - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_READ_INPUT_REGS; - _frame[1] = _len - 2; - - word val; - word i = 0; - while(numregs--) { - //retrieve the value from the register bank for the current register - val = this->Ireg(startreg + i); - //write the high byte of the register value - _frame[2 + i * 2] = val >> 8; - //write the low byte of the register value - _frame[3 + i * 2] = val & 0xFF; - i++; - } - - _reply = MB_REPLY_NORMAL; -} - -void Modbus::writeSingleCoil(word reg, word status) { - //Check value (status) - if (status != 0xFF00 && status != 0x0000) { - this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address and execute (reg exists?) - if (!this->Coil(reg, (bool)status)) { - this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_ADDRESS); - return; - } - - //Check for failure - if (this->Coil(reg) != (bool)status) { - this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_SLAVE_FAILURE); - return; - } - - _reply = MB_REPLY_ECHO; -} - -void Modbus::writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount) { - //Check value - word bytecount_calc = numoutputs / 8; - if (numoutputs%8) bytecount_calc++; - if (numoutputs < 0x0001 || numoutputs > 0x07B0 || bytecount != bytecount_calc) { - this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_VALUE); - return; - } - - //Check Address (startreg...startreg + numregs) - for (int k = 0; k < numoutputs; k++) { - if (!this->searchRegister(startreg + 1 + k)) { - this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_ADDRESS); - return; - } - } - - //Clean frame buffer - free(_frame); - _len = 5; - _frame = (byte *) malloc(_len); - if (!_frame) { - this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_SLAVE_FAILURE); - return; - } - - _frame[0] = MB_FC_WRITE_COILS; - _frame[1] = startreg >> 8; - _frame[2] = startreg & 0x00FF; - _frame[3] = numoutputs >> 8; - _frame[4] = numoutputs & 0x00FF; - - byte bitn = 0; - word totoutputs = numoutputs; - word i; - while (numoutputs) { - i = (totoutputs - numoutputs) / 8; - this->Coil(startreg, bitRead(frame[6+i], bitn)); - //increment the bit index - bitn++; - if (bitn == 8) bitn = 0; - //increment the register - startreg++; - numoutputs--; - } - - _reply = MB_REPLY_NORMAL; -} -#endif - - - From 91af31cbe5b56fb71b3abb5cb8d569d30b15e6c2 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Thu, 5 Jan 2017 22:21:02 +0330 Subject: [PATCH 07/10] fixing multiple coils,registers --- libraries/Modbus/Modbus.cpp | 517 ++++++++++++++++++++++++++++++++++++ libraries/Modbus/Modbus.h | 100 +++++++ 2 files changed, 617 insertions(+) create mode 100644 libraries/Modbus/Modbus.cpp create mode 100644 libraries/Modbus/Modbus.h diff --git a/libraries/Modbus/Modbus.cpp b/libraries/Modbus/Modbus.cpp new file mode 100644 index 0000000..fd82671 --- /dev/null +++ b/libraries/Modbus/Modbus.cpp @@ -0,0 +1,517 @@ +/* + Modbus.cpp - Source for Modbus Base Library + Copyright (C) 2014 André Sarmento Barbosa +*/ +#include "Modbus.h" + +Modbus::Modbus() { + _regs_head = 0; + _regs_last = 0; +} + +TRegister* Modbus::searchRegister(word address) { + TRegister *reg = _regs_head; + //if there is no register configured, bail + if(reg == 0) return(0); + //scan through the linked list until the end of the list or the register is found. + //return the pointer. + do { + if (reg->address == address) return(reg); + reg = reg->next; + } while(reg); + return(0); +} + +void Modbus::addReg(word address, word value) { + TRegister *newreg; + + newreg = (TRegister *) malloc(sizeof(TRegister)); + newreg->address = address; + newreg->value = value; + newreg->next = 0; + + if(_regs_head == 0) { + _regs_head = newreg; + _regs_last = _regs_head; + } else { + //Assign the last register's next pointer to newreg. + _regs_last->next = newreg; + //then make temp the last register in the list. + _regs_last = newreg; + } +} + +bool Modbus::Reg(word address, word value) { + TRegister *reg; + //search for the register address + reg = this->searchRegister(address); + //if found then assign the register value to the new value. + if (reg) { + reg->value = value; + return true; + } else + return false; +} + +word Modbus::Reg(word address) { + TRegister *reg; + reg = this->searchRegister(address); + if(reg) + return(reg->value); + else + return(0); +} + +void Modbus::addHreg(word offset, word value) { + this->addReg(offset + 40001, value); +} + +bool Modbus::Hreg(word offset, word value) { + return Reg(offset + 40001, value); +} + +word Modbus::Hreg(word offset) { + return Reg(offset + 40001); +} + +#ifndef USE_HOLDING_REGISTERS_ONLY + void Modbus::addCoil(word offset, bool value) { + this->addReg(offset + 1, value?0xFF00:0x0000); + } + + void Modbus::addIsts(word offset, bool value) { + this->addReg(offset + 10001, value?0xFF00:0x0000); + } + + void Modbus::addIreg(word offset, word value) { + this->addReg(offset + 30001, value); + } + + bool Modbus::Coil(word offset, bool value) { + return Reg(offset + 1, value?0xFF00:0x0000); + } + + bool Modbus::Ists(word offset, bool value) { + return Reg(offset + 10001, value?0xFF00:0x0000); + } + + bool Modbus::Ireg(word offset, word value) { + return Reg(offset + 30001, value); + } + + bool Modbus::Coil(word offset) { + if (Reg(offset + 1) == 0xFF00) { + return true; + } else return false; + } + + bool Modbus::Ists(word offset) { + if (Reg(offset + 10001) == 0xFF00) { + return true; + } else return false; + } + + word Modbus::Ireg(word offset) { + return Reg(offset + 30001); + } +#endif + + +void Modbus::receivePDU(byte* frame) { + byte fcode = frame[0]; + word field1 = (word)frame[1] << 8 | (word)frame[2]; + word field2 = (word)frame[3] << 8 | (word)frame[4]; + + switch (fcode) { + + case MB_FC_WRITE_REG: + //field1 = reg, field2 = value + this->writeSingleRegister(field1, field2); + break; + + case MB_FC_READ_REGS: + //field1 = startreg, field2 = numregs + this->readRegisters(field1, field2); + break; + + case MB_FC_WRITE_REGS: + //field1 = startreg, field2 = status + this->writeMultipleRegisters(frame,field1, field2, frame[5]); + break; + + #ifndef USE_HOLDING_REGISTERS_ONLY + case MB_FC_READ_COILS: + //field1 = startreg, field2 = numregs + this->readCoils(field1, field2); + break; + + case MB_FC_READ_INPUT_STAT: + //field1 = startreg, field2 = numregs + this->readInputStatus(field1, field2); + break; + + case MB_FC_READ_INPUT_REGS: + //field1 = startreg, field2 = numregs + this->readInputRegisters(field1, field2); + break; + + case MB_FC_WRITE_COIL: + //field1 = reg, field2 = status + this->writeSingleCoil(field1, field2); + break; + + case MB_FC_WRITE_COILS: + //field1 = startreg, field2 = numoutputs + this->writeMultipleCoils(frame,field1, field2, frame[5]); + break; + + #endif + default: + this->exceptionResponse(fcode, MB_EX_ILLEGAL_FUNCTION); + } +} + +void Modbus::exceptionResponse(byte fcode, byte excode) { + //Clean frame buffer + free(_frame); + _len = 2; + _frame = (byte *) malloc(_len); + _frame[0] = fcode + 0x80; + _frame[1] = excode; + + _reply = MB_REPLY_NORMAL; +} + +void Modbus::readRegisters(word startreg, word numregs) { + //Check value (numregs) + if (numregs < 0x0001 || numregs > 0x007D) { + this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address + //*** See comments on readCoils method. + if (!this->searchRegister(startreg + 40001)) { + this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_ADDRESS); + return; + } + + + //Clean frame buffer + free(_frame); + _len = 0; + + //calculate the query reply message length + //for each register queried add 2 bytes + _len = 2 + numregs * 2; + + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_READ_REGS, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_READ_REGS; + _frame[1] = _len - 2; //byte count + + word val; + word i = 0; + while(numregs--) { + //retrieve the value from the register bank for the current register + val = this->Hreg(startreg + i); + //write the high byte of the register value + _frame[2 + i * 2] = val >> 8; + //write the low byte of the register value + _frame[3 + i * 2] = val & 0xFF; + i++; + } + + _reply = MB_REPLY_NORMAL; +} + +void Modbus::writeSingleRegister(word reg, word value) { + //No necessary verify illegal value (EX_ILLEGAL_VALUE) - because using word (0x0000 - 0x0FFFF) + //Check Address and execute (reg exists?) + if (!this->Hreg(reg, value)) { + this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_ILLEGAL_ADDRESS); + return; + } + + //Check for failure + if (this->Hreg(reg) != value) { + this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_SLAVE_FAILURE); + return; + } + + _reply = MB_REPLY_ECHO; +} + +void Modbus::writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount) { + //Check value + if (numoutputs < 0x0001 || numoutputs > 0x007B || bytecount != 2 * numoutputs) { + this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address (startreg...startreg + numregs) + for (int k = 0; k < numoutputs; k++) { + if (!this->searchRegister(startreg + 40001 + k)) { + this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_ADDRESS); + return; + } + } + + //Clean frame buffer + free(_frame); + _len = 5; + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_WRITE_REGS; + _frame[1] = startreg >> 8; + _frame[2] = startreg & 0x00FF; + _frame[3] = numoutputs >> 8; + _frame[4] = numoutputs & 0x00FF; + + word val; + word i = 0; + while(numoutputs--) { + val = (word)frame[6+i*2] << 8 | (word)frame[7+i*2]; + this->Hreg(startreg + i, val); + i++; + } + + _reply = MB_REPLY_NORMAL; +} + +#ifndef USE_HOLDING_REGISTERS_ONLY +void Modbus::readCoils(word startreg, word numregs) { + //Check value (numregs) + if (numregs < 0x0001 || numregs > 0x07D0) { + this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address + //Check only startreg. Is this correct? + //When I check all registers in range I got errors in ScadaBR + //I think that ScadaBR request more than one in the single request + //when you have more then one datapoint configured from same type. + if (!this->searchRegister(startreg + 1)) { + this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); + return; + } + + //Clean frame buffer + free(_frame); + _len = 0; + + //Determine the message length = function type, byte count and + //for each group of 8 registers the message length increases by 1 + _len = 2 + numregs/8; + if (numregs%8) _len++; //Add 1 to the message length for the partial byte. + + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_READ_COILS, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_READ_COILS; + _frame[1] = _len - 2; //byte count (_len - function code and byte count) + + byte bitn = 0; + word totregs = numregs; + word i; + while (numregs) { + i = (totregs - numregs) / 8; + if (this->Coil(startreg)) + bitSet(_frame[2+i], bitn); + else + bitClear(_frame[2+i], bitn); + //increment the bit index + bitn++; + if (bitn == 8) bitn = 0; + //increment the register + startreg++; + numregs--; + } + + _reply = MB_REPLY_NORMAL; +} + +void Modbus::readInputStatus(word startreg, word numregs) { + //Check value (numregs) + if (numregs < 0x0001 || numregs > 0x07D0) { + this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address + //*** See comments on readCoils method. + if (!this->searchRegister(startreg + 10001)) { + this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); + return; + } + + //Clean frame buffer + free(_frame); + _len = 0; + + //Determine the message length = function type, byte count and + //for each group of 8 registers the message length increases by 1 + _len = 2 + numregs/8; + if (numregs%8) _len++; //Add 1 to the message length for the partial byte. + + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_READ_INPUT_STAT; + _frame[1] = _len - 2; + + byte bitn = 0; + word totregs = numregs; + word i; + while (numregs) { + i = (totregs - numregs) / 8; + if (this->Ists(startreg)) + bitSet(_frame[2+i], bitn); + else + bitClear(_frame[2+i], bitn); + //increment the bit index + bitn++; + if (bitn == 8) bitn = 0; + //increment the register + startreg++; + numregs--; + } + + _reply = MB_REPLY_NORMAL; +} + +void Modbus::readInputRegisters(word startreg, word numregs) { + //Check value (numregs) + if (numregs < 0x0001 || numregs > 0x007D) { + this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address + //*** See comments on readCoils method. + if (!this->searchRegister(startreg + 30001)) { + this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS); + return; + } + + //Clean frame buffer + free(_frame); + _len = 0; + + //calculate the query reply message length + //for each register queried add 2 bytes + _len = 2 + numregs * 2; + + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_READ_INPUT_REGS; + _frame[1] = _len - 2; + + word val; + word i = 0; + while(numregs--) { + //retrieve the value from the register bank for the current register + val = this->Ireg(startreg + i); + //write the high byte of the register value + _frame[2 + i * 2] = val >> 8; + //write the low byte of the register value + _frame[3 + i * 2] = val & 0xFF; + i++; + } + + _reply = MB_REPLY_NORMAL; +} + +void Modbus::writeSingleCoil(word reg, word status) { + //Check value (status) + if (status != 0xFF00 && status != 0x0000) { + this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address and execute (reg exists?) + if (!this->Coil(reg, (bool)status)) { + this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_ADDRESS); + return; + } + + //Check for failure + if (this->Coil(reg) != (bool)status) { + this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_SLAVE_FAILURE); + return; + } + + _reply = MB_REPLY_ECHO; +} + +void Modbus::writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount) { + //Check value + word bytecount_calc = numoutputs / 8; + if (numoutputs%8) bytecount_calc++; + if (numoutputs < 0x0001 || numoutputs > 0x07B0 || bytecount != bytecount_calc) { + this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_VALUE); + return; + } + + //Check Address (startreg...startreg + numregs) + for (int k = 0; k < numoutputs; k++) { + if (!this->searchRegister(startreg + 1 + k)) { + this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_ADDRESS); + return; + } + } + + //Clean frame buffer + free(_frame); + _len = 5; + _frame = (byte *) malloc(_len); + if (!_frame) { + this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_SLAVE_FAILURE); + return; + } + + _frame[0] = MB_FC_WRITE_COILS; + _frame[1] = startreg >> 8; + _frame[2] = startreg & 0x00FF; + _frame[3] = numoutputs >> 8; + _frame[4] = numoutputs & 0x00FF; + + byte bitn = 0; + word totoutputs = numoutputs; + word i; + while (numoutputs) { + i = (totoutputs - numoutputs) / 8; + this->Coil(startreg, bitRead(frame[6+i], bitn)); + //increment the bit index + bitn++; + if (bitn == 8) bitn = 0; + //increment the register + startreg++; + numoutputs--; + } + + _reply = MB_REPLY_NORMAL; +} +#endif + + + diff --git a/libraries/Modbus/Modbus.h b/libraries/Modbus/Modbus.h new file mode 100644 index 0000000..a52c875 --- /dev/null +++ b/libraries/Modbus/Modbus.h @@ -0,0 +1,100 @@ +/* + Modbus.h - Header for Modbus Base Library + Copyright (C) 2014 André Sarmento Barbosa +*/ +#include "Arduino.h" + +#ifndef MODBUS_H +#define MODBUS_H + +#define MAX_REGS 32 +#define MAX_FRAME 128 +//#define USE_HOLDING_REGISTERS_ONLY + +typedef unsigned int u_int; + +//Function Codes +enum { + MB_FC_READ_COILS = 0x01, // Read Coils (Output) Status 0xxxx + MB_FC_READ_INPUT_STAT = 0x02, // Read Input Status (Discrete Inputs) 1xxxx + MB_FC_READ_REGS = 0x03, // Read Holding Registers 4xxxx + MB_FC_READ_INPUT_REGS = 0x04, // Read Input Registers 3xxxx + MB_FC_WRITE_COIL = 0x05, // Write Single Coil (Output) 0xxxx + MB_FC_WRITE_REG = 0x06, // Preset Single Register 4xxxx + MB_FC_WRITE_COILS = 0x0F, // Write Multiple Coils (Outputs) 0xxxx + MB_FC_WRITE_REGS = 0x10, // Write block of contiguous registers 4xxxx +}; + +//Exception Codes +enum { + MB_EX_ILLEGAL_FUNCTION = 0x01, // Function Code not Supported + MB_EX_ILLEGAL_ADDRESS = 0x02, // Output Address not exists + MB_EX_ILLEGAL_VALUE = 0x03, // Output Value not in Range + MB_EX_SLAVE_FAILURE = 0x04, // Slave Deive Fails to process request +}; + +//Reply Types +enum { + MB_REPLY_OFF = 0x01, + MB_REPLY_ECHO = 0x02, + MB_REPLY_NORMAL = 0x03, +}; + +typedef struct TRegister { + word address; + word value; + struct TRegister* next; +} TRegister; + +class Modbus { + private: + TRegister *_regs_head; + TRegister *_regs_last; + + void readRegisters(word startreg, word numregs); + void writeSingleRegister(word reg, word value); + void writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount); + void exceptionResponse(byte fcode, byte excode); + #ifndef USE_HOLDING_REGISTERS_ONLY + void readCoils(word startreg, word numregs); + void readInputStatus(word startreg, word numregs); + void readInputRegisters(word startreg, word numregs); + void writeSingleCoil(word reg, word status); + void writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount); + #endif + + TRegister* searchRegister(word addr); + + void addReg(word address, word value = 0); + bool Reg(word address, word value); + word Reg(word address); + + protected: + byte *_frame; + byte _len; + byte _reply; + void receivePDU(byte* frame); + + public: + Modbus(); + + void addHreg(word offset, word value = 0); + bool Hreg(word offset, word value); + word Hreg(word offset); + + #ifndef USE_HOLDING_REGISTERS_ONLY + void addCoil(word offset, bool value = false); + void addIsts(word offset, bool value = false); + void addIreg(word offset, word value = 0); + + bool Coil(word offset, bool value); + bool Ists(word offset, bool value); + bool Ireg(word offset, word value); + + bool Coil(word offset); + bool Ists(word offset); + word Ireg(word offset); + #endif +}; + +#endif //MODBUS_H From ba8652c4120135282a502f2821deedd46b929321 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Wed, 3 May 2017 09:59:00 +0430 Subject: [PATCH 08/10] new commit From 9e124a2ef7e6e06f5ad360691205ef0d04df92a0 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Wed, 3 May 2017 10:04:56 +0430 Subject: [PATCH 09/10] new commit --- libraries/ModbusIP/ModbusIP.cpp | 12 +----------- libraries/ModbusIP/ModbusIP.h | 9 +++------ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/libraries/ModbusIP/ModbusIP.cpp b/libraries/ModbusIP/ModbusIP.cpp index 9a5b4b0..1460967 100644 --- a/libraries/ModbusIP/ModbusIP.cpp +++ b/libraries/ModbusIP/ModbusIP.cpp @@ -5,7 +5,6 @@ #include "ModbusIP.h" ModbusIP::ModbusIP():_server(MODBUSIP_PORT) { - _firstClient = false; } void ModbusIP::config(uint8_t *mac) { @@ -37,7 +36,6 @@ void ModbusIP::task() { EthernetClient client = _server.available(); if (client) { - _firstClient = true; if (client.connected()) { int i = 0; while (client.available()){ @@ -83,13 +81,5 @@ void ModbusIP::task() { free(_frame); _len = 0; } - if(client != _client) - { - if(_firstClient) - { - _client.stop(); - } - _client = client; - } } -} +} \ No newline at end of file diff --git a/libraries/ModbusIP/ModbusIP.h b/libraries/ModbusIP/ModbusIP.h index caa99a2..4bd26a5 100644 --- a/libraries/ModbusIP/ModbusIP.h +++ b/libraries/ModbusIP/ModbusIP.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #ifndef MODBUSIP_H #define MODBUSIP_H @@ -13,13 +13,11 @@ #define MODBUSIP_PORT 502 #define MODBUSIP_MAXFRAME 200 -//#define TCP_KEEP_ALIVE +#define TCP_KEEP_ALIVE class ModbusIP : public Modbus { private: - EthernetClient _client; EthernetServer _server; - bool _firstClient; byte _MBAP[7]; public: @@ -32,5 +30,4 @@ class ModbusIP : public Modbus { void task(); }; -#endif //MODBUSIP_H - +#endif //MODBUSIP_H \ No newline at end of file From 8cad7abb14f1a7d583241169a47156dfe7efdbb9 Mon Sep 17 00:00:00 2001 From: Seyed Amir Alavi Date: Sun, 31 Dec 2017 00:48:39 +0000 Subject: [PATCH 10/10] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7c5c286..ddfec48 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Read/Write Multiple Coil/Inputs Fixed! (8 bit bug) Modbus Library for Arduino ========================== +Bugs in main repo are fixed regarding to read/write for more than 8 coils. + This library allows your Arduino to communicate via Modbus protocol. The Modbus is a master-slave protocol used in industrial automation and can be used in other areas, such as home automation.