Modbus and I2C communication With Arduino

In my project, I need to program my Arduino (Mega 2560) through Simulink and need to send sensor data and take control commands from Micro Lab Box (over Ethernet Modbus).
For this, I have used two Arduino, one programmed in Simulink to do I2C communication with other Arduino and the Second one is programmed using Arduino IDE for I2C as well as Modbus communication.

Arduino1(Simulink)<—I2C send and Receive–>Arduino2(IDE)<----I2C and Modbus send Receive---->MLB

Here, the problem is When only Arduino1 is in I2C receive mode I can able send control commands over Modbus from MLB via Arduino2. But as soon as, Arduino 1 is in writing mode this function is not working, only sensor data I can read over Modbus to the MLB.

I have attached here my program code for Arduino2, and a screenshot of the Simulink layout for Arduino 1.
Arduino1 - I2C Master
Arduino2 - I2C slave, Modbus server
MLB - -Modbus client

i2c_slave_eth_modbus_server.ino (2.42 KB)

void receiveEvent() {
  for (int i = 0; i < sizeof(I2C_receiveData); i++) {
    I2C_receiveData[i] = (byte)Wire.read();
    modbusTCPServer.holdingRegisterWrite(Write_Add + i, I2C_receiveData[i]);
//    Serial.println("I2C Data");
//    Serial.println(I2C_receiveData[i]);
    modbusTCPServer.poll();
  }
}

receiveEvent is called in interrupt context. You must not call any method that depends on interrupts to work (as methods of the Serial object or modbusTCPServer object).

You have to shorten the code in that routine to just receive the I2C data, store it and set a flag so that code in the loop() can handle it.

Thank you very much pylon for the correction. I will try as you suggested and give an update.

Although that I2C receive (and than Modbus TCP/IP send) is functioning well with my old version code. The problem comes when Receive from Modbus TCP/IP and then send over I2C. This happens also because of the coding sequence is wrong?

Although that I2C receive (and than Modbus TCP/IP send) is functioning well with my old version code.

I don’t know the old version of your code but the posted version calls network code inside interrupt context which will fail.

The problem comes when Receive from Modbus TCP/IP and then send over I2C.

That part is even worse. During the whole execution of the requestEvent() routine the Arduino will stretch the I2C clock, so it will block the complete I2C bus. As that is executed also in interrupt context you must avoid any network code too.

void requestEvent() {
  modbusTCPServer.poll();
  for (int x = 0; x < sizeof(MLB_receiveData); x++) {
    modbusTCPServer.poll();
    MLB_receiveData[x] = (byte)(modbusTCPServer.holdingRegisterRead(Start_Add + x));
    Wire.write(MLB_receiveData[x]);
  }
  modbusTCPServer.poll();
}

You cannot request the data from the Modbus device when the I2C master requests it. That will need too much time even if we ignore the problem with interrupt context.
So you have to constantly read the Modbus registers and store them locally on the Arduino to have them ready when it’s asked for them by the I2C bus.

Thank you pylon.
I changed my code accordingly to your suggestions and also try it out.

The problem is still there.

When I2C master Arduino doesn’t want to write anything over the I2C bus ( receive event is absent on I2C slave Arduino), at that time I am able to send data MLB (Micro Lab Box) over an Ethernet Modbus and receive that data on I2C Master Arduino.

As soon as I enable to receive the event on I2C slave the upper describe function stops working and just write event from I2C master happens.

Here is my new code

#include <Wire.h>
#include <Ethernet.h>
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>

byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x79, 0x65};
byte ip[] = {169, 254, 80, 5};
EthernetServer EthServer(502);
ModbusTCPServer modbusTCPServer;
int Write_Add = 11;     // address from where Modbus server (Arduio) writes data on holding register and from where Modbus client (MLB) reads
int Start_Add = 0;     // address from where Modbus server (Arduio) reads data from holding register that Modbus client (MLB) has written
int Slave_Add = 9;

volatile byte I2C_receiveData[7] = {0, 0, 0, 0, 0, 0, 0}; //act pressure, fan speeds from i2C master
volatile byte MLB_receiveData[3] = {0, 0, 0}; //set pressure with sign info from MLB


//Arduino1 one tries to write data to MLB over Modbus via Arduino2(with I2C) 
void receiveEvent() {
 for (int i = 0; i < sizeof(I2C_receiveData); i++) {
   I2C_receiveData[i] = (byte)Wire.read();
 }
}

//Arduino1 one tries to read data via Arduino2(with I2C)from MLB over Modbus
void requestEvent() {
 for (int x = 0; x < sizeof(MLB_receiveData); x++) {
   Wire.write(MLB_receiveData[x]);
 }
}

void setup()
{
 Ethernet.begin(mac, ip); // initialize the ethernet device
 EthServer.begin();          // start listening for clients
 modbusTCPServer.begin();    // start listening for clients
 modbusTCPServer.configureHoldingRegisters(Start_Add, 20);
 Serial.begin(9600);           //start serial communication with baud rate of 9600
 Wire.begin(Slave_Add);
 
 Wire.onRequest(requestEvent);
 Wire.onReceive(receiveEvent);
}

void loop() {
 EthernetClient client = EthServer.available();
 while (client.connected()) {
   modbusTCPServer.accept(client);
   modbusTCPServer.poll();// poll for Modbus TCP requests, while client connected
 
   for (int x = 0; x < sizeof(MLB_receiveData); x++) {
     MLB_receiveData[x] = (byte)(modbusTCPServer.holdingRegisterRead(Start_Add + x));
   Serial.println(MLB_receiveData[x]);
   }
for (int i = 0; i < sizeof(I2C_receiveData); i++) {
   modbusTCPServer.holdingRegisterWrite(Write_Add + i, I2C_receiveData[i]);
 }
 }
 delay(500);
}

Edit your post and change the quote tags to code tags. As you can see the forum system otherwise doesn't show the code correctly!

I edited in the same previous post

     MLB_receiveData[x] = (byte)(modbusTCPServer.holdingRegisterRead(Start_Add + x));

A Modbus register is 16bit wide, so this line contains an information lost.

When I2C master Arduino doesn't want to write anything over the I2C bus ( receive event is absent on I2C slave Arduino), at that time I am able to send data MLB (Micro Lab Box) over an Ethernet Modbus and receive that data on I2C Master Arduino.

As soon as I enable to receive the event on I2C slave the upper describe function stops working and just write event from I2C master happens.

I'm not sure I understood what the problem is. It seems that the I2C slave Arduino stops working if you send something from the I2C master Arduino to it. If that is your problem, post the code you run on the I2C master!

A Modbus register is 16bit wide, so this line contains information lost.

In my case, it causes no problem as I am sending only uint8 data bytes.

It seems that the I2C slave Arduino stops working if you send something from the I2C master Arduino to

Yes, I am not sure but I think because of the different hardware interrupts, under I2C master sent event I2C slave works on receive event. The master code is in Matlab Simulink as in the attached picture, it reads data at a sample rate of 0.05sec (50msec).

The Modbus-client part is also in Matlab Simulink, which will be in write mode whenever data changes by user otherwise it will be in read-mode. It's sample rate is 0.001sec (1msec)

Yes, I am not sure but I think because of the different hardware interrupts, under I2C master sent event I2C slave works on receive event. The master code is in Matlab Simulink as in the attached picture, it reads data at a sample rate of 0.05sec (50msec).

So how do you know that the I2C slave stops working?

Remove the delay(500) call, it's completely unnecessary.

So how do you know that the I2C slave stops working?

From the slave Arduino function, i removed the I2C receive function and then checked the Modbus receive and I2C sent part. All worked perfectly.

Remove the delay(500) call, it's completely unnecessary.

I need to put some delay otherwise, Modbus server Arduino (I2C slave) is not receiving data from the client (MLB.)

I need to put some delay otherwise, Modbus server Arduino (I2C slave) is not receiving data from the client (MLB.)

The code you posted is the I2C slave. I don't see any reason why it shouldn't receive any data if you delete the delay() line but it works with that delay (except if you've done the Modbus client completely wrong).