Arduino Modbus TCP

Now I have a piece of code that works, but I'm not going to use registers alone, for example Write Coil, I don't know how to set variables to write coil parameters. Please help me how to program to write coil values.

#include <SPI.h>
#include <Ethernet2.h>
#include "MgsModbus.h"
MgsModbus Mb;
int inByte = 0; // incoming serial byte

// Ethernet settings (depending on MAC and Local network)
byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB5 };
IPAddress ip(192, 168, 1, 177);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);


void setup()
{
  // serial setup
  Serial.begin(9600);
  Serial.println("Serial interface started");
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);   // start etehrnet interface
  Serial.println("Ethernet interface started"); 

  // print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print("."); 
  }
  Serial.println();

  // Fill MbData
//  Mb.SetBit(0,false);
  Mb.MbData[0] = 0;
  Mb.MbData[1] = 0;
  Mb.MbData[2] = 0;
  Mb.MbData[3] = 0;
  Mb.MbData[4] = 0;
  Mb.MbData[5] = 0;
  Mb.MbData[6] = 0;
  Mb.MbData[7] = 0;
  Mb.MbData[8] = 0;
  Mb.MbData[9] = 0;
  Mb.MbData[10] = 0;
  Mb.MbData[11] = 0;
  
  // print MbData
  for (int i=0;i<12;i++) {
    Serial.print("address: "); Serial.print(i); Serial.print("Data: "); Serial.println(Mb.MbData[i]);
  }
  // print menu
  Serial.println("0 - print the first 12 words of the MbData space");
  Serial.println("1 - fill MbData with 0x0000 hex");
  Serial.println("2 - fill MbData with 0xFFFF hex");
  Serial.println("3 - fill MbData with 0x5555 hex");
  Serial.println("4 - fill MbData with 0xAAAA hex");
}

void loop()
{
  if (Serial.available() > 0) {
    // get incoming byte:
    inByte = Serial.read();
    if (inByte == '0') {                                          // print MbData
      for (int i=0;i<12;i++) {
        Serial.print("address: "); Serial.print(i); Serial.print("Data: "); Serial.println(Mb.MbData[i]);
      }
    }  
    if (inByte == '1') {for (int i=0;i<12;i++) {Mb.MbData[i] = 0x0000;}}  
    if (inByte == '2') {for (int i=0;i<12;i++) {Mb.MbData[i] = 0xFFFF;}}  
    if (inByte == '3') {for (int i=0;i<12;i++) {Mb.MbData[i] = 0x5555;}}  
    if (inByte == '4') {for (int i=0;i<12;i++) {Mb.MbData[i] = 0xAAAA;}}  
  }

//  Mb.MbmRun();
  Mb.MbsRun();
}
#include "MgsModbus.h"

// For Arduino 1.0
EthernetServer MbServer(MB_PORT);
EthernetClient MbmClient;

// #define DEBUG

MgsModbus::MgsModbus()
{
}


//****************** Send data for ModBusMaster****************
void MgsModbus::Req(MB_FC FC, word Ref, word Count, word Pos)
{
  MbmFC = FC;
  byte ServerIp[] = {192,168,1,177};
  MbmByteArray[0] = 0;  // ID high byte
  MbmByteArray[1] = 1;  // ID low byte
  MbmByteArray[2] = 0;  // protocol high byte
  MbmByteArray[3] = 0;  // protocol low byte
  MbmByteArray[5] = 6;  // Lenght low byte;
  MbmByteArray[4] = 0;  // Lenght high byte
  MbmByteArray[6] = 1;  // unit ID
  MbmByteArray[7] = FC; // function code
  MbmByteArray[8] = highByte(Ref);
  MbmByteArray[9] = lowByte(Ref);
  //****************** Read Coils (1) & Read Input discretes (2) **********************
  if(FC == MB_FC_READ_COILS || FC == MB_FC_READ_DISCRETE_INPUT) {
    if (Count < 1) {Count = 1;}
    if (Count > 125) {Count = 2000;}
    MbmByteArray[10] = highByte(Count);
    MbmByteArray[11] = lowByte(Count);
  }
  //****************** Read Registers (3) & Read Input registers (4) ******************
  if(FC == MB_FC_READ_REGISTERS || FC == MB_FC_READ_INPUT_REGISTER) {
    if (Count < 1) {Count = 1;}
    if (Count > 125) {Count = 125;}
    MbmByteArray[10] = highByte(Count);
    MbmByteArray[11] = lowByte(Count);
  }
  //****************** Write Coil (5) **********************
  if(MbmFC == MB_FC_WRITE_COIL) {
    if (GetBit(Pos)) {MbmByteArray[10] = 0xff;} else {MbmByteArray[10] = 0;} // 0xFF coil on 0x00 coil off 
    MbmByteArray[11] = 0; // always zero 
   
  }
  //****************** Write Register (6) ******************
  if(MbmFC == MB_FC_WRITE_REGISTER) {
    MbmByteArray[10] = highByte(MbData[Pos]);
    MbmByteArray[11] = lowByte(MbData[Pos]);
  }
  //****************** Write Multiple Coils (15) **********************
  // not fuly tested
  if(MbmFC == MB_FC_WRITE_MULTIPLE_COILS) {
    if (Count < 1) {Count = 1;}
    if (Count > 800) {Count = 800;}
    MbmByteArray[10] = highByte(Count);
    MbmByteArray[11] = lowByte(Count);
    MbmByteArray[12] = (Count + 7) /8;
    MbmByteArray[4] = highByte(MbmByteArray[12] + 7); // Lenght high byte
    MbmByteArray[5] = lowByte(MbmByteArray[12] + 7); // Lenght low byte;
    for (int i=0; i<Count; i++) {
      bitWrite(MbmByteArray[13+(i/8)],i-((i/8)*8),GetBit(Pos+i));
    }
  }
  //****************** Write Multiple Registers (16) ******************
  if(MbmFC == MB_FC_WRITE_MULTIPLE_REGISTERS) {
    if (Count < 1) {Count = 1;}
    if (Count > 100) {Count = 100;}
    MbmByteArray[10] = highByte(Count);
    MbmByteArray[11] = lowByte(Count);
    MbmByteArray[12] = (Count*2);
    MbmByteArray[4] = highByte(MbmByteArray[12] + 7); // Lenght high byte
    MbmByteArray[5] = lowByte(MbmByteArray[12] + 7); // Lenght low byte;
    for (int i=0; i<Count;i++) {
      MbmByteArray[(i*2)+13] = highByte (MbData[Pos + i]);
      MbmByteArray[(i*2)+14] = lowByte (MbData[Pos + i]);
    }
  }
  //****************** ?? ******************
  if (MbmClient.connect(ServerIp,502)) {
    #ifdef DEBUG
      Serial.println("connected with modbus slave");
      Serial.print("Master request: ");
      for(int i=0;i<MbmByteArray[5]+6;i++) {
        if(MbmByteArray[i] < 16){Serial.print("0");}
        Serial.print(MbmByteArray[i],HEX);
        if (i != MbmByteArray[5]+5) {Serial.print(".");} else {Serial.println();}
      }
    #endif    
    for(int i=0;i<MbmByteArray[5]+6;i++) {
      MbmClient.write(MbmByteArray[i]);
    }
    MbmCounter = 0;
    MbmByteArray[7] = 0;
    MbmPos = Pos;
    MbmBitCount = Count;
  } else {
    #ifdef DEBUG
      Serial.println("connection with modbus master failed");
    #endif    
    MbmClient.stop();
  }
}


//****************** Recieve data for ModBusMaster ****************
void MgsModbus::MbmRun()
{
  //****************** Read from socket ****************
  while (MbmClient.available()) {
    MbmByteArray[MbmCounter] = MbmClient.read();
    if (MbmCounter > 4)  {
      if (MbmCounter == MbmByteArray[5] + 5) { // the full answer is recieved  
        MbmClient.stop();
        MbmProcess();
        #ifdef DEBUG
          Serial.println("recieve klaar");
        #endif    
      }
    }
    MbmCounter++;
  }
}

void MgsModbus::MbmProcess()
{
  MbmFC = SetFC(int (MbmByteArray[7]));
  #ifdef DEBUG
    for (int i=0;i<MbmByteArray[5]+6;i++) {
      if(MbmByteArray[i] < 16) {Serial.print("0");}
      Serial.print(MbmByteArray[i],HEX);
      if (i != MbmByteArray[5]+5) {Serial.print(".");
      } else {Serial.println();}
    }
  #endif    
  //****************** Read Coils (1) & Read Input discretes (2) **********************
  if(MbmFC == MB_FC_READ_COILS || MbmFC == MB_FC_READ_DISCRETE_INPUT) {
    word Count = MbmByteArray[8] * 8;
    if (MbmBitCount < Count) {
      Count = MbmBitCount;
    }
    for (int i=0;i<Count;i++) {
      if (i + MbmPos < MbDataLen * 16) {
        SetBit(i + MbmPos,bitRead(MbmByteArray[(i/8)+9],i-((i/8)*8)));
      }
    }
  }
  //****************** Read Registers (3) & Read Input registers (4) ******************
  if(MbmFC == MB_FC_READ_REGISTERS || MbmFC == MB_FC_READ_INPUT_REGISTER) {
    word Pos = MbmPos;
    for (int i=0;i<MbmByteArray[8];i=i+2) {
      if (Pos < MbDataLen) {
        MbData[Pos] = (MbmByteArray[i+9] * 0x100) + MbmByteArray[i+1+9];
        Pos++;
      }
    }
  }
  //****************** Write Coil (5) **********************
  if(MbmFC == MB_FC_WRITE_COIL){

  }
  //****************** Write Register (6) ******************
  if(MbmFC == MB_FC_WRITE_REGISTER){

  }
  //****************** Write Multiple Coils (15) **********************
  if(MbmFC == MB_FC_WRITE_MULTIPLE_COILS){
  }
  //****************** Write Multiple Registers (16) ******************
  if(MbmFC == MB_FC_WRITE_MULTIPLE_REGISTERS){
  }
}


//****************** Recieve data for ModBusSlave ****************
void MgsModbus::MbsRun()
{  
  //****************** Read from socket ****************
  EthernetClient client = MbServer.available();
  if(client.available())
  {
    delay(10);
    int i = 0;
    while(client.available())
    {
      MbsByteArray[i] = client.read();
      i++;
    }
    MbsFC = SetFC(MbsByteArray[7]);  //Byte 7 of request is FC
  }
  int Start, WordDataLength, ByteDataLength, CoilDataLength, MessageLength;
  //****************** Read Coils (1 & 2) **********************
  if(MbsFC == MB_FC_READ_COILS || MbsFC == MB_FC_READ_DISCRETE_INPUT) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]);
    ByteDataLength = CoilDataLength / 8;
    if(ByteDataLength * 8 < CoilDataLength) ByteDataLength++;      
    CoilDataLength = ByteDataLength * 8;
    MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
    MbsByteArray[8] = ByteDataLength;     //Number of bytes after this one (or number of bytes of data).
    for(int i = 0; i < ByteDataLength ; i++)
    {
      MbsByteArray[9 + i] = 0; // To get all remaining not written bits zero
      for(int j = 0; j < 8; j++)
      {
        bitWrite(MbsByteArray[9 + i], j, GetBit(Start + i * 8 + j));
      }
    }
    MessageLength = ByteDataLength + 9;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  }
  //****************** Read Registers (3 & 4) ******************
  if(MbsFC == MB_FC_READ_REGISTERS || MbsFC == MB_FC_READ_INPUT_REGISTER) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    WordDataLength = word(MbsByteArray[10],MbsByteArray[11]);
    ByteDataLength = WordDataLength * 2;
    MbsByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
    MbsByteArray[8] = ByteDataLength;     //Number of bytes after this one (or number of bytes of data).
    for(int i = 0; i < WordDataLength; i++)
    {
      MbsByteArray[ 9 + i * 2] = highByte(MbData[Start + i]);
      MbsByteArray[10 + i * 2] =  lowByte(MbData[Start + i]);
    }
    MessageLength = ByteDataLength + 9;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  }
  //****************** Write Coil (5) **********************
  if(MbsFC == MB_FC_WRITE_COIL) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    if (word(MbsByteArray[10],MbsByteArray[11]) == 0xFF00){SetBit(Start,true);}
    if (word(MbsByteArray[10],MbsByteArray[11]) == 0x0000){SetBit(Start,false);}
    MbsByteArray[5] = 2; //Number of bytes after this one.
    MessageLength = 8;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  
  } 
  //****************** Write Register (6) ******************
  if(MbsFC == MB_FC_WRITE_REGISTER) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    MbData[Start] = word(MbsByteArray[10],MbsByteArray[11]);
    MbsByteArray[5] = 6; //Number of bytes after this one.
    MessageLength = 12;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  }
  //****************** Write Multiple Coils (15) **********************
  if(MbsFC == MB_FC_WRITE_MULTIPLE_COILS) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    CoilDataLength = word(MbsByteArray[10],MbsByteArray[11]);
    MbsByteArray[5] = 6;
    for(int i = 0; i < CoilDataLength; i++)
    {
      SetBit(Start + i,bitRead(MbsByteArray[13 + (i/8)],i-((i/8)*8)));
    }
    MessageLength = 12;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  }  
  //****************** Write Multiple Registers (16) ******************
  if(MbsFC == MB_FC_WRITE_MULTIPLE_REGISTERS) {
    Start = word(MbsByteArray[8],MbsByteArray[9]);
    WordDataLength = word(MbsByteArray[10],MbsByteArray[11]);
    ByteDataLength = WordDataLength * 2;
    MbsByteArray[5] = 6;
    for(int i = 0; i < WordDataLength; i++)
    {
      MbData[Start + i] =  word(MbsByteArray[ 13 + i * 2],MbsByteArray[14 + i * 2]);
    }
    MessageLength = 12;
    client.write(MbsByteArray, MessageLength);
    MbsFC = MB_FC_NONE;
  }
}


//****************** ?? ******************
MB_FC MgsModbus::SetFC(int fc)
{
  MB_FC FC;
  FC = MB_FC_NONE;
  if(fc == 1) FC = MB_FC_READ_COILS;
  if(fc == 2) FC = MB_FC_READ_DISCRETE_INPUT;
  if(fc == 3) FC = MB_FC_READ_REGISTERS;
  if(fc == 4) FC = MB_FC_READ_INPUT_REGISTER;
  if(fc == 5) FC = MB_FC_WRITE_COIL;
  if(fc == 6) FC = MB_FC_WRITE_REGISTER;
  if(fc == 15) FC = MB_FC_WRITE_MULTIPLE_COILS;
  if(fc == 16) FC = MB_FC_WRITE_MULTIPLE_REGISTERS;
  return FC;
}

 
word MgsModbus::GetDataLen()
{
  return MbDataLen;
}
 
 
boolean MgsModbus::GetBit(word Number)
{
  int ArrayPos = Number / 16;
  int BitPos = Number - ArrayPos * 16;
  boolean Tmp = bitRead(MbData[ArrayPos],BitPos);
  return Tmp;
}


boolean MgsModbus::SetBit(word Number,boolean Data)
{
  int ArrayPos = Number / 16;
  int BitPos = Number - ArrayPos * 16;
  boolean Overrun = ArrayPos > MbDataLen * 16; // check for data overrun
  if (!Overrun){                 
    bitWrite(MbData[ArrayPos],BitPos,Data);
  } 
  return Overrun;
}
/*
  MgsModbus.h - an Arduino library for a Modbus TCP master and slave.
  V-0.1.1 Copyright (C) 2013  Marco Gerritse
  written and tested with Arduino 1.0
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

  For this library the following library is used as start point:
  
    [1] Mudbus.h - an Arduino library for a Modbus TCP slave.
        Copyright (C) 2011  Dee Wykoff
    [2] Function codes 15 & 16 by Martin Pettersson
    
  The following references are used to write this library:
  
    [3] Open Modbus/Tcp specification, Release 1.0, 29 March 1999
        By Andy Swales, Schneider Electric
    [4] Modbus application protocol specification V1.1b3, 26 april 202
        From http:/www.modbus.org
        
  External software used for testing:
  
    [5] modpoll - www.modbusdriver.com/modpoll.html
    [6] ananas - www.tuomio.fi/ananas
    [7] mod_rssim - www.plcsimulator.org 
    [8] modbus master - www.cableone.net/mblansett/
    
  This library use a single block of memory for all modbus data (mbData[] array). The
  same data can be reached via several modbus functions, either via a 16 bit access
  or via an access bit. The length of MbData must at least 1.
  
  For the master the following modbus functions are implemented: 1, 2, 3, 4, 5, 6, 15, 16
  For the slave the following modbus functions are implemented: 1, 2, 3, 4, 5, 6, 15, 16
  
  The internal and external addresses are 0 (zero) based
  
  
  V-0.1.1 2013-06-02
  bugfix
  
  V-0.1.0 2013-03-02
  initinal version
*/


#include "Arduino.h"

#include <SPI.h>
#include <Ethernet2.h>

#ifndef MgsModbus_h
#define MgsModbus_h

#define MbDataLen 30 // length of the MdData array
#define MB_PORT 502

enum MB_FC {
  MB_FC_NONE                     = 0,
  MB_FC_READ_COILS               = 1,
  MB_FC_READ_DISCRETE_INPUT      = 2,
  MB_FC_READ_REGISTERS           = 3,
  MB_FC_READ_INPUT_REGISTER      = 4,
  MB_FC_WRITE_COIL               = 5,
  MB_FC_WRITE_REGISTER           = 6,
  MB_FC_WRITE_MULTIPLE_COILS     = 15,
  MB_FC_WRITE_MULTIPLE_REGISTERS = 16
};

class MgsModbus
{
public:
  // general
  MgsModbus();
  word MbData[MbDataLen]; // memory block that holds all the modbus user data
  boolean GetBit(word Number);
  boolean SetBit(word Number,boolean Data); // returns true when the number is in the MbData range
  // modbus master
  void Req(MB_FC FC, word Ref, word Count, word Pos);
  void MbmRun();
  IPAddress remSlaveIP;
  // modbus slave
  void MbsRun();  
  word GetDataLen();
private: 
  // general
  MB_FC SetFC(int fc);
  // modbus master 
  uint8_t MbmByteArray[260]; // send and recieve buffer
  MB_FC MbmFC;
  int MbmCounter;
  void MbmProcess();
  word MbmPos;
  word MbmBitCount;
  //modbus slave
  uint8_t MbsByteArray[260]; // send and recieve buffer
  MB_FC MbsFC;
};

#endif

@guochen , your topic has been moved to a more suitable location on the forum. Installation and Troubleshooting is not for problems with (nor for advise on) your project.

Where is the problem? The library you're using stores the coil values in the same array as the holding registers so you have to take care of that when setting the register addresses.
To set a single coil value, you can use the SetBit(address, value) method of the library.

  • Thank you, I have tried, can not use the library format to write.

What does "to write" mean? Do you want to write into the register array on the slave (server) side or do you want to write a register from the master (client) on the slave (server) using Modbus?

I want to use Arduino to write into the register array ,For example, data from arduino I/O is written to a specified register, inputRegisterWrite(int id, int address). The program uses an array, I don't understand how to write in this program, okay

I already wrote how you can set coil bits.

If the actual question is how you know that the master has written a register, then I have bad news for you: you have to check the MbData array for changes. There is no such thing as a callback in the library you've chosen.

  • Yeah, that's the problem so far, I can't see how arrays change, okay.I also don't know how to define how to use array transfer. It's too difficult for beginners

In this case define what you want to achieve. So far you described how you want to achieve it but what the reason? Tell use more about your project. Maybe we can find a solution, maybe we can suggest an alternative library.

My project uses coil reads and writes,holdingRegisterWrite,holdingRegisterWrite.holdingRegisterWrite use variable int and float.Arduino Get the simulation value of the sensor,Then use an ADC to convert,through uesing holdingRegisterWrite(int or float) to send variables.(Official library routines modbus tcp holdingRegisterWrite can only use int .I don't know how to write using floating point values.It would also be great if you could tell me how the official library uses float)Thank you my friend!

That's still a description of how you think it should be done but not of what you actually want to achieve. Forget the Modbus and stuff and describe what circuit you have and in what environment you want to use it and what for.

Arduino ,w5500 , Modbus TCP is used for communication in the wincc.Arduino Obtain the voltage current value over Ethernet to WinCC.Voltage and current have a decimal point, so I want to use float, but the official example is just int,

Still a list of components you think are useful for the task but no description of the task itself.

That won't involve a Modbus write operation.

Voltage and current? Of what?

The Modbus standard knows only 16bit integers. If can transfer float in two register values but that's always a proprietary extension which has to be documented separately.

I use the sample code above, but when I compile the software, I get the following error: MgsModbus.cpp:4:16: error: cannot declare variable 'MbServer' to be of abstract type 'EthernetServer'
EthernetServer MbServer(MB_PORT);
Who knows can help me

Probably you didn't install the Ethernet2 library you're including or the IDE cannot find it. Enable verbose compiling and post the output.

I did like this and it gives error like on the picture

This is text output, why the hell are you posting it as a picture?

It seems that the IDE is not using your Ethernet2 library. So you either installed it incorrectly or there's another reason for the IDE to ignore it. It probably told you why in another section of the output. That's why you should post the text and not a picture showing only a small part of it.

I have both Ethernet and Ethernet2 libraries installed in Arduino

And the IDE is not using it. As you still failed to post the complete output we had no chance to see why that happened.