Modbus tips for Industruino

Split 32-bit values into 16-bit registers

Tom

Modbus is a popular industrial serial communications protocol, available over Ethernet as Modbus TCP and over RS485 as Modbus RTU. The Industruino INDIO has an RS485 port so can act as a Modbus RTU Master or Slave. If you want to use Modbus TCP, you will need to add an Ethernet module to your Industruino PROTO or INDIO.

For examples, see:

Modbus uses registers to pass data between the Master and the Slave(s). The Master is sending out periodic requests; the Slave is listening for requests, reading and/or writing to the registers. The registers can have different indeces on Master and Slave(s). The registers are 16-bit (standard unsigned integer on Arduino), so if you want to pass data of 32-bit (float or long integer), you need to use 2 registers per value, by splitting the 32-bit value into 2x 16-bit. 

NOTE: This would make sense if you have high precision numbers, e.g. a 18-bit ADC reading, the highest setting of the INDIO. If you use less than 18-bit precision, it makes sense to convert your float to a 16-bit integer and use only 1 register. E.g. you can get a precision of 3 decimal digits in 4-20mA readings by multiplying them by 1000 and converting to int type. This would get you a maximum value of 20000 which fits in the 16-bit int type. 

Here are 2 pieces of Arduino code to handle the conversion of 32-bit FLOAT and LONG numbers to 16-bit unsigned integers used for Modbus registers. Use this code in your Master and Slave units as needed. It should be easy to modify it for 32-bit unsigned long type numbers too.

FLOAT TYPE

/*
 * INDUSTRUINO ModBus help (D21G)
 * modbus registers are 16-bit unsigned integers
 * to pass a float variable (32-bit), it needs to be split into 2 registers
 * use the below functions to achieve this
 * Tom Tobback Nov 2017
 */


void setup() {

  SerialUSB.begin(9600);
  while (!SerialUSB);
}

void loop() {

  SerialUSB.print("Enter a float number in the Serial Monitor: ");

  float float_number = 0;
  while (float_number == 0) {
    float_number = SerialUSB.parseFloat();                     // get a float from Serial Monitor input
  }
  SerialUSB.println(float_number, 15);                         // print with 15 decimals

  unsigned int reg0 = f_2uint_int1(float_number);           // split the float into 2 unsigned integers
  unsigned int reg1 = f_2uint_int2(float_number);

  SerialUSB.print("Float number (32-bit) to be converted: ");
  SerialUSB.println(float_number);
  SerialUSB.print("Split into 2 unsigned integers (16-bit): ");
  SerialUSB.print(reg0);
  SerialUSB.print("\t");
  SerialUSB.println(reg1);

  float float_reconstructed = f_2uint_float(reg0, reg1);    // reconstruct the float from 2 unsigned integers

  SerialUSB.print("Reconstructed to float (32-bit): ");
  SerialUSB.println(float_reconstructed, 15);

  SerialUSB.println();
}

unsigned int f_2uint_int1(float float_number) {             // split the float and return first unsigned integer

  union f_2uint {
    float f;
    uint16_t i[2];
  };

  union f_2uint f_number;
  f_number.f = float_number;

  return f_number.i[0];
}

unsigned int f_2uint_int2(float float_number) {            // split the float and return first unsigned integer

  union f_2uint {
    float f;
    uint16_t i[2];
  };

  union f_2uint f_number;
  f_number.f = float_number;

  return f_number.i[1];
}

float f_2uint_float(unsigned int uint1, unsigned int uint2) {    // reconstruct the float from 2 unsigned integers

  union f_2uint {
    float f;
    uint16_t i[2];
  };

  union f_2uint f_number;
  f_number.i[0] = uint1;
  f_number.i[1] = uint2;

  return f_number.f;

}


LONG TYPE (with sign)

/*
 * INDUSTRUINO ModBus help (D21G)
 * modbus registers are 16-bit unsigned integers
 * to pass a long variable (32-bit), it needs to be split into 2 registers
 * use the below functions to achieve this
 * Tom Tobback Nov 2017
 */

void setup() {

  SerialUSB.begin(9600);
  while (!SerialUSB);

}

void loop() {

  SerialUSB.print("Enter a 'long' number in the Serial Monitor: ");

  long long_number = 0;
  while (long_number == 0) {
    long_number = SerialUSB.parseInt();                     // get a long from Serial Monitor input
  }
  SerialUSB.println(long_number);                         // print 

  unsigned int reg0 = l_2uint_int1(long_number);           // split the long into 2 unsigned integers
  unsigned int reg1 = l_2uint_int2(long_number);

  SerialUSB.print("Long number (32-bit) to be converted: ");
  SerialUSB.println(long_number);
  SerialUSB.print("Split into 2 unsigned integers (16-bit): ");
  SerialUSB.print(reg0);
  SerialUSB.print("\t");
  SerialUSB.println(reg1);

  long long_reconstructed = l_2uint_long(reg0, reg1);    // reconstruct the long from 2 unsigned integers

  SerialUSB.print("Reconstructed to long (32-bit): ");
  SerialUSB.println(long_reconstructed);

  SerialUSB.println();
}

unsigned int l_2uint_int1(long long_number) {             // split the long and return first unsigned integer

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.l = long_number;

  return l_number.i[0];
}

unsigned int l_2uint_int2(long long_number) {            // split the long and return first unsigned integer

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.l = long_number;

  return l_number.i[1];
}

long l_2uint_long(unsigned int uint1, unsigned int uint2) {    // reconstruct the long from 2 unsigned integers

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.i[0] = uint1;
  l_number.i[1] = uint2;

  return l_number.l;

}