I2C to Modbus using Arduino Micro

Hello All. I am currently working on a project that uses both I2C communication and Modbus Communication protocols. I am using a custom Arduino Micro (incorporates arduino micro, I2C multiplexer, and RS485 Shield.)
The project is reading up to 48 I2C sensors using an I2C multiplexer (if you need my hardware specifications please let me know.) It takes this data, does some quick calculations and then packages it for Modbus communication. Note the Arduino is acting as a Modbus RTU slave while a koyo click PLC is the Modbus Master. Using Half Duplex RS485.
I have been able to get the I2C side to work without the Modbus, and the Modbus side to work without the I2C side. But when I bring them together it writes the program, and then seems to crash the bootloader. (Warning pops up in windows that says the USB device I have plugged in is not recognizable, warning is resolved when I reload the bootloader.)
Notes:

  1. the issue seems to be tied to the wire.write function. if I remove these, the code uploads and the modbus side works again. Looking through the libraries I did not see an overlap but perhaps I missed something??
  2. To get this to work on the Micro I had to edit the Modbus Library to use Serial1 rather than Serial0 (Rx and Tx pins on micro are tied to serial1)
    Code is posted below, please let me know what other information I can provide. Thank you for your help in advance!!


#include <modbus.h>
#include <modbusDevice.h>
#include <modbusRegBank.h>
#include <modbusSlave.h>
#include <Wire.h>
#include <EEPROM.h>

modbusDevice regBank;
modbusSlave slave;

#define Mem_ADDR  0x10      //I2C address for memory card
#define Stored_Slave_ADDR 0x11     //Address within memory card where slave ID is stored
#define MUX_ADDR 0x70                                             //Multiplexer address
#define AutoPin 4

uint8_t zone;
uint8_t axis;
uint16_t Position = 0;
uint16_t Position1 = 0;
uint16_t ZStart;
uint16_t ZEnd;
uint16_t Maximum_T;
uint8_t nAvg = 1;                                                 //Number of measurements to average the readings over - Should always be 1 in this program
uint16_t fB_x;
uint16_t fB_y;
uint16_t fB_z;
uint16_t fTemp;
int Counter;
uint8_t Slave_ID = 1;
uint8_t nDevAddr;                                                //Sensor address used to read - pulled from address arrays below
uint32_t nRegData;                                               //Storage Bit for data read from 0x27 on sensors - used to set full loop mode
int i = 0;
int L = 0;
uint16_t Mem_Read;
bool New_BaseE;
bool New_BaseF;
bool gauss;
int NumSense = 48;                                                // Total number of sensors in use
uint8_t NumBus;                                                   // Number of I2C buses used
//I2C Sensor Adress Array
uint8_t Adress_Array[] = {96, 100, 104, 108, 99, 103, 107, 0, 98, 102, 106, 110, 97, 101, 105, 109};                     //Percentage for each zone

//I2C write to sensor code
uint16_t I2C_write(uint8_t nAddr, uint8_t nRegAddr, uint32_t nValue = 0, uint8_t nSize = 0);
//I2C read sensor code
uint16_t I2C_read(uint8_t nAddr, uint8_t nRegAddr, uint32_t& nValue, uint8_t nSize);
//I2C Quick Sensor read (fast loop mode, only reads msb)
uint16_t I2C_read(uint8_t nAddr, uint32_t& nValue, uint8_t nSize);
//Code to kick off read of MSB, LSB.  Seperate the data into the correct parts, combine the MSB and LSB.  Return four discrete values.
uint16_t readSensor(uint8_t nAddr, float& fX, float& fY, float& fZ, float& fTemp);

void setup()
{


  i = 0;                                                        //BUS COUNTER
  L = 0;                                                        //ADDRESS COUNTER
  for ( i = 0; i < 3; i++)                                 //LOOPS THROUGH EACH BUS (I2c MUX ADDRESS)
  {
    Wire.beginTransmission(MUX_ADDR);
    Wire.write(1 << i);
    Wire.endTransmission();                                               //SELECTS CORRECT I2C CHANNEL (SUBROUTINE USED IN MAIN LOOP AS WELL)
    for (L = 0; L < 16; L++)                           //LOOP TO INITIALIZE EACH SENSOR IN FAST READ MODE
    {
      nDevAddr = Adress_Array[L];
      intSensor2();                             // Write bits back to sensor                                           //SUBROUTINE THAT INTIALIZES EACH SENSOR
    }
  }
  regBank.setId(1);                    //Set Slave ID
  regBank.add(1);//SET BASELINE EMPTY
  regBank.add(2);//SET BASELINE FULL
  regBank.add(3);//gauss mode

  i = 0;
  for (i = 0; i < 6; i++) //Zone, axis, % and temperature Reg.
  {
    regBank.add(i + 40001);
  }
  i = 0;
  for (i = 0; i < 16; i++) //sensor data registers
  {
    regBank.add(i + 40007);
  }                     //ADD ALL NEEDED REGISTERS HERE.
  //pinMode(AutoPin,OUTPUT);
  //digitalWrite(AutoPin, HIGH);
  slave._device = &regBank;
  slave.setBaud(9600);
}


void loop() {

  Read_Mod();   //Read values placed in Registers from PLC
  Set_Zone();    //Sets the correct zone based on a register from PLC, all sub routines loop by zone
  if (New_BaseF == 1) //Sets a new full magnet baseline if required
  {
    Baseline();
  }
  if (New_BaseE == 1) //Sets a new Empty baseline if required
  {
    Baseline();
  }
  if (gauss == 1) //Read gauss of all sensors if required
  {
    Gauss_Mode();
  }
  Percentage();   //creates a percentage for each zone
  slave.run();    //runs the modbus send/recieve protocol
}

void Gauss_Mode ()//reads sensors and sets value to correct regBank Based on zone and axis from PLC.
{
  Counter = 0;
  intSensor();
  for (L = ZStart; L < ZEnd; L++)
  {
    fB_x = 0.f;
    fB_y = 0.f;
    fB_z = 0.f;
    fTemp = 0.f;
    nDevAddr = Adress_Array[L];
    readSensor(nDevAddr, fB_x, fB_y, fB_z, fTemp);
    if (axis = 0)
    {
      regBank.set(40007 + Counter, fB_x);
    }
    else if (axis = 1)
    {
      regBank.set(40007 + Counter, fB_y);
    }
    else if (axis = 2)
    {
      regBank.set(40007 + Counter, fB_z);
    }
    else if (axis = 3)
    {
      regBank.set(40007 + Counter, fTemp);
    }
    Counter = Counter + 1;
  }
}

void Set_Zone()//selects the sensors the PLC is calling for, adjust numbers manually for each magnet length
{
  if (zone == 1)
  {
    ZStart = 0;
    ZEnd = 16;
    NumBus = 0;
  }
  else if (zone == 2)
  {
    ZStart = 0;
    ZEnd = 16;
    NumBus = 1;
  }
  else if (zone == 3)
  {
    ZStart = 0;
    ZEnd = 16;
    NumBus = 2;
  }
}

void intSensor2()                                                 //setting each sensor to full loop mode
{
  Wire.begin(nDevAddr);
  Wire.setClock(100000);
  uint16_t nError = I2C_write(nDevAddr, 0x35, 0x2C413534);            //Enable customer writing to sensor - Default code from manufacturer
  I2C_read(nDevAddr, 0x27, nRegData, 4);                              //Read data from 0X27 Register
  nRegData &= ~(0x11 << 2);                                           // clear and Set Full loop mode bits
  nRegData |= 0x02 << 2;
  I2C_write(nDevAddr, 0x27, nRegData, 4);                             // Write bits back to sensor
}


void intSensor ()    //sets the MUX address
{
  Wire.beginTransmission(MUX_ADDR);
  Wire.write(1 << i);
  Wire.endTransmission();


}


void Set_New_Slave_ID()
{

  regBank.setId(Slave_ID);
}


void Baseline()
{
  if (zone == 1)
  {

    if (New_BaseE == 1)
    {
      Position = 0;
    }
    else if (New_BaseF == 1)
    {
      Position = NumSense * 3;
    }
  }
  Counter = 0;
  intSensor();
  for (L = ZStart; L < ZEnd; L++)
  {
    fB_x = 0.f;
    fB_y = 0.f;
    fB_z = 0.f;
    fTemp = 0.f;
    nDevAddr = Adress_Array[L];
    readSensor(nDevAddr, fB_x, fB_y, fB_z, fTemp);
    if (axis = 0)
    {
      regBank.set(40007 + Counter, fB_x);
      Mem_Read = EEPROM.read(Position);
      if (Mem_Read != fB_x)
      {
        EEPROM.write(Position, fB_x);

      }
    }
    else if (axis = 1)
    {
      regBank.set(40007 + Counter, fB_y);
      Mem_Read = EEPROM.read(Position + NumSense);
      if (Mem_Read != fB_x)
      {
        EEPROM.write(Position + NumSense, fB_y);

      }
    }
    else if (axis = 2)
    {
      regBank.set(40007 + Counter, fB_z);
      Mem_Read = EEPROM.read(Position + NumSense * 2);
      if (Mem_Read != fB_x)
      {
        EEPROM.write(Position + NumSense * 2, fB_z);

      }
    }
    Counter = Counter + 1;
    Position = Position + 1;
  }

}



void Percentage()       //Note only uses the Z axis data at this time.
{
  if (zone == 1)
  {
    Position1 = NumSense * 2;
  }
  int MAXP = 0;
  int SumP = 0;
  int MAXT = 0;
  int Percentage;
  intSensor();
  for (L = ZStart; L < ZEnd; L++)
  {
    fB_x = 0.f;
    fB_y = 0.f;
    fB_z = 0.f;
    fTemp = 0.f;
    nDevAddr = Adress_Array[L];
    readSensor(nDevAddr, fB_x, fB_y, fB_z, fTemp);
    Percentage = 100 * sqrt((EEPROM.read(Position1) - fB_z) ^ 2 / (EEPROM.read(Position1) - EEPROM.read(Position1 + NumSense * 3))); //Percentage function (Base-Current)/(Base-Full)*100
    SumP = SumP + Percentage; //Sum of all the percentages in the zone
    if (fTemp > MAXT) //Finds Maximum Temperature reading in the zone
    {
      MAXT = fTemp;
      if (MAXT > Maximum_T)
      {
        Maximum_T = MAXT;
      }
    }
    if (Percentage > MAXP) //Looks for maximum percentage reading in a specific zone
    {
      MAXP = Percentage;
    }
  }
  regBank.set(40003, MAXP);
  regBank.set(40004, SumP / ZEnd);
  regBank.set(40005, MAXT);
  regBank.set(40006, Maximum_T);
  if (Maximum_T > EEPROM.read(150))
  {
    EEPROM.write(150, Maximum_T);
  }
  Position1 = Position1 + 1;

}

void Read_Mod()  //Code to read Modbus data from PLC.
{
  if (New_BaseE != regBank.get(1))
  {
    New_BaseE = regBank.get(1);
    Position = 0;
  }
  if (New_BaseF != regBank.get(1))
  {
    New_BaseF = regBank.get(1);
    Position = NumSense * 3;
  }
  gauss = regBank.get(3);
  zone = regBank.get(40001); // tells controller which zone to send
  axis = regBank.get(40002); //Tells controller what axis of data to send
}


uint16_t readSensor(uint8_t nAddr, uint16_t& fX, uint16_t& fY, uint16_t& fZ, uint16_t& fTemp)
{
  uint32_t nData_MSB = 0;                                                 //Initialize Most Significant Bits
  uint32_t nData_LSB = 0;                                                 //Initialize Least Significant Bits
  //Initialize variables where data readings are stored
  uint16_t nX_2s = 0;
  uint16_t nY_2s = 0;
  uint16_t nZ_2s = 0;
  uint16_t nT_2s = 0;

  //read register 0x28 and 0x29 - Registers containing hall effect and temperature data
  I2C_read(nDevAddr, 0x28, nData_MSB, 4);
  I2C_read(nDevAddr, 0x29, nData_LSB, 4);

  //Extract and shift MSB to final position
  nX_2s = (nData_MSB & 0x7f000000) >> 20;
  nY_2s = (nData_MSB & 0x007f0000) >> 12;
  nZ_2s = (nData_MSB & 0x00007f00) >> 4;
  nT_2s = (nData_MSB & 0x0000003f) << 6;

  //Extract LSB and Add to MSB
  nX_2s |= (nData_LSB & 0x000f0000) >> 16;
  nY_2s |= (nData_LSB & 0x0000f000) >> 12;
  nZ_2s |= (nData_LSB & 0x00000f00) >> 8;
  nT_2s |= (nData_LSB & 0x0000003f);
  //Cast data to final variables and transform to gauss and celcius
  fX += static_cast<float>(nX_2s);
  fY += static_cast<float>(nY_2s);
  fZ += static_cast<float>(nZ_2s);
  fTemp += 302.f / 4096.f * (static_cast<float>(nT_2s) - 1708.f);
  Serial.println(fZ);
  return 0;
}


uint16_t I2C_write(uint8_t nAddr, uint8_t nRegAddr, uint32_t nValue, uint8_t nSize)
{
  Wire.beginTransmission(nAddr);
  Wire.write(nRegAddr);

  for (uint8_t i = nSize - 1; i > 0; i--)
  {
    Wire.write((byte)(nValue >> (8 * i)));
  }

  return Wire.endTransmission();
}


uint16_t I2C_read(uint8_t nAddr, uint8_t nRegAddr, uint32_t& nValue, uint8_t nSize)
{
  // Write the address that is to be read to the device
  Wire.beginTransmission(nAddr);
  Wire.write(nRegAddr);
  int nError = Wire.endTransmission();

  nValue = 0;

  if (!nError)
  {
    Wire.requestFrom(nAddr, nSize);

    for (int8_t i = nSize - 1; i >= 0; i--)
    {
      nValue |= Wire.read() << (8 * i);
    }
  }

  return nError;
}


uint16_t I2C_read(uint8_t nAddr, uint32_t& nValue, uint8_t nSize)
{
  nValue = 0;

  for (int8_t i = nSize - 1; i >= 0; i--)
  {
    nValue |= Wire.read() << (8 * i);
  }

  return 0;
}

For me both would be first indication that you're writing outside the boundaries of arrays. I however could not find it in the code.

Which modbus library are you using? I can't compile your code because I don't know so can't check for warnings.

MODBUS.zip (10.0 KB)

See attached for the Modbus Library I am using. here is the link for it on GitHub:

Before the first Wire.beginTransmission(), there is no Wire.begin().
The Wire.begin() enables also the Slave mode. Why ?
The Wire.begin() is called 16 times. Why ?
The Wire.begin() is called while there is already I2C communication. You must have a very good reason to do that.

Do you know what Wire.begin() does ?
https://www.arduino.cc/en/Reference/WireBegin
There is also my alternative explanation.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.