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 TCP over Ethernet
- Modbus RTU over RS485: INDIO as Master, and communication between 2 or more INDIOs
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; }