Arduino Modbus RTU RS485 with TUF-2000M

Hi Everyone:

Hope you are all doing fine!

I come to you for your kind help and/or advice…

I’ve been working some time trying to get data out of a TUF-2000M ultrasonic Flow Meter with and arduino using the Modbus RTU protocol over RS485. So far I think I’m able to connect to the device accordingly but i can not make sense of the data I’m receiving back from it…

here is my code…

#include <SimpleModbusMaster.h>

/* To communicate with a slave you need to create a
packet that will contain all the information
required to communicate to that slave.

There are numerous counters for easy diagnostic.
These are variables already implemented in a
packet. You can set and clear these variables
as needed.

There are general modbus information counters:
requests - contains the total requests to a slave
successful_requests - contains the total successful requests
total_errors - contains the total errors as a sum
timeout - contains the total time out errors
incorrect_id_returned - contains the total incorrect id returned errors
incorrect_function_returned - contains the total incorrect function returned errors
incorrect_bytes_returned - contains the total incorrect bytes returned errors
checksum_failed - contains the total checksum failed errors
buffer_errors - contains the total buffer errors

And there are modbus specific exception counters:
illegal_function - contains the total illegal function errors
illegal_data_address - contains the total illegal data_address errors
illegal_data_value - contains the total illegal data value errors
misc_exceptions - contains the total miscellaneous returned exceptions

And finally there is a variable called “connection” that
at any given moment contains the current connection
status of the packet. If true then the connection is
active if false then communication will be stopped
on this packet untill the programmer sets the “connection”
variable to true explicitly. The reason for this is
because of the time out involved in modbus communication.
EACH faulty slave that’s not communicating will slow down
communication on the line with the time out value. E.g.
Using a time out of 1500ms, if you have 10 slaves and 9 of them
stops communicating the latency burden placed on communication
will be 1500ms * 9 = 13,5 seconds!!!

modbus_update() returns the previously scanned false connection.
You can use this as the index to your packet array to find out
if the connection has failed in that packet and then react to it.
You can then try to re-enable the connecion by setting the
packet->connection attribute to true.
The index will only be available for one loop cycle, after that
it’s cleared and ready to return the next false connection index
if there is one else it will return the packet array size indicating
everything is ok.

All the error checking, updating and communication multitasking
takes place in the background!

In general to communicate with to a slave using modbus
RTU you will request information using the specific
slave id, the function request, the starting address
and lastly the number of registers to request.
Function 3 and 16 are supported. In addition to
this broadcasting (id = 0) is supported on function 16.
Constants are provided for:
Function 3 - READ_HOLDING_REGISTERS
Function 16 - PRESET_MULTIPLE_REGISTERS

The example sketch will read a packet consisting
of 3 registers from address 0 using function 3 from
the GM7U LS Industrial PLC (id = 2) and then write
another packet containing the same 3 registers and
the counter information of both packets using function 16
to address 3 in the PLC. Using the supplied PLC software
you can then view in realtime what the values are in
the PLC’s registers.
*/

// led to indicate that a communication error is present
#define connection_error_led 13

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 500 // the scan rate

// If the packets internal retry register matches
// the set retry count then communication is stopped
// on that packet. To re-enable the packet you must
// set the “connection” variable to true.
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 4

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
PACKET1,
//PACKET2,
// leave this last entry
TOTAL_NO_OF_PACKETS
};

// Create an array of Packets for modbus_update()
Packet packets[TOTAL_NO_OF_PACKETS];

// Create a packetPointer to access each packet
// individually. This is not required you can access
// the array explicitly. E.g. packets[PACKET1].id = 2;
// This does become tedious though…
packetPointer packet1 = &packets[PACKET1];
//packetPointer packet2 = &packets[PACKET2];

// The data from the PLC will be stored
// in the regs array
unsigned int regs[9];

void setup()
{
// read 3 registers starting at address 0
packet1->id = 1;
packet1->function = READ_HOLDING_REGISTERS;
packet1->address = 138;
packet1->no_of_registers = 1;
packet1->register_array = regs;

/* // write the 9 registers to the PLC starting at address 3
packet2->id = 2;
packet2->function = PRESET_MULTIPLE_REGISTERS;
packet2->address = 3;
packet2->no_of_registers = 9;
packet2->register_array = regs;*/

// P.S. the register_array entries above can be different arrays

// Initialize communication settings etc…
modbus_configure(baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);

pinMode(connection_error_led, OUTPUT);
}

void loop()
{
unsigned int connection_status = modbus_update(packets);

if (connection_status != TOTAL_NO_OF_PACKETS)
{
digitalWrite(connection_error_led, HIGH);
// You could re-enable the connection by:
//packets[connection_status].connection = true;
}
else
digitalWrite(connection_error_led, LOW);

// update the array with the counter data
regs[3] = packet1->requests;
regs[4] = packet1->successful_requests;
regs[5] = packet1->total_errors;
/* regs[6] = packet2->requests;
regs[7] = packet2->successful_requests;
regs[8] = packet2->total_errors; */
Serial.print("Requests: ");
Serial.println(regs[3]);
Serial.print("Successful: ");
Serial.println(regs[4]);
Serial.print("Errors: ");
Serial.println(regs[5]);
Serial.print("data 1: ");
Serial.println(regs[0]);
Serial.print("data 2: ");
Serial.println(regs[1]);
Serial.print("data 3: ");
Serial.println(regs[2]);
Serial.print("data 4: ");
Serial.println(regs[6]);
Serial.println(regs[7]);
Serial.println(regs[8]);
Serial.println(regs[9]);
Serial.println(regs[0]);
float temp = (long)regs[0] << 16 | regs[1];
Serial.println(temp);
float temp1 = (long)regs[1] << 16 | regs[0];
Serial.println(temp1);
delay(1000);
}

and Here is the output i get…

Requests: 15
Successful: 14
Errors: 1
data 1: 31376
data 2: 0
data 3: 0
data 4: 0
0
0
769
31376
2056257536.00
31376.00

In this code I’m getting data out of register 138 that corresponds to total flow of the day… the value in the device is 2.8339 but i get in regs[0] 31376…

I’m pretty sure my data is somehow encoded in the regs[0] but i do not know the format of the information I’m receiving… maybe the 31376 is a decimal representation of a hexadecimal code or something…

the 769 is a value i always get no matter what register i get data from… so i guess that contains the address and funcion … (769 to hex is 0301 03 being the read registers function and 01 the id of the device… but i’m not sure…

So any light on how to get the data i’m looking for is very appreciated.

Thank you very much.

you should have linked a manual to your device you want to read! (!!!)

you are reading:

 packet1->address = 138;
  packet1->no_of_registers = 1;

only register 138.

but flow for today is defined in the manual as following:
0137-0138
2
Flow for today
LONG

this means you have to start to read at 137, 2 bytes and but the two bytes together to get one long value.

One more thing,
you have commented out
//PACKET2,

but you are still using it in the setup().

And additionally, use the regs array only for values read by packets, use other variables for the status (if even necessary):

regs[3] = packet1->requests;
regs[4] = packet1->successful_requests;
regs[5] = packet1->total_errors;

u

Thanks for your response and time Noiasca:

sorry i did not include the manual…

i have read from the two registers 137-138
packet1->id = 1;
packet1->function = READ_HOLDING_REGISTERS;
packet1->address = 137;
packet1->no_of_registers = 2;
packet1->register_array = regs;

and combined them like so…

long temp = (long)regs[0] << 16 | (long)regs[1];

the results are…

data 1: 0
data 2: 31376
data 3: 0
data 4: 0
0
0
769
0
31376

and if i combine them in reverse order like

long temp = (long)regs[1] << 16 | (long)regs[0];

i get

Successful: 0
Errors: 0
data 1: 0
data 2: 31376
data 3: 0
data 4: 0
0
0
769
0
2056257536

in both cases the las number is the combined long int…

still can not figure out how to interpret the data i’m getting…

thanks

Hi …

thanks for your time…

here is the link to the manual…

here is the code without the comment…

#include <SimpleModbusMaster.h>

// led to indicate that a communication error is present
#define connection_error_led 13

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 500 // the scan rate

// If the packets internal retry register matches
// the set retry count then communication is stopped
// on that packet. To re-enable the packet you must
// set the "connection" variable to true.
#define retry_count 10 

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 4 

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  //PACKET2,
  // leave this last entry
  TOTAL_NO_OF_PACKETS
};

// Create an array of Packets for modbus_update()
Packet packets[TOTAL_NO_OF_PACKETS];

// Create a packetPointer to access each packet
// individually. This is not required you can access
// the array explicitly. E.g. packets[PACKET1].id = 2;
// This does become tedious though...
packetPointer packet1 = &packets[PACKET1];
//packetPointer packet2 = &packets[PACKET2];

// The data from the PLC will be stored
// in the regs array
unsigned int regs[9];

void setup()
{
  // read 3 registers starting at address 0
  packet1->id = 1;
  packet1->function = READ_HOLDING_REGISTERS;
  packet1->address = 137;
  packet1->no_of_registers = 2;
  packet1->register_array = regs;
  
 /* // write the 9 registers to the PLC starting at address 3
  packet2->id = 2;
  packet2->function = PRESET_MULTIPLE_REGISTERS;
  packet2->address = 3;
  packet2->no_of_registers = 9;
  packet2->register_array = regs;*/
  
  // P.S. the register_array entries above can be different arrays
  
  // Initialize communication settings etc...
  modbus_configure(baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
  
  pinMode(connection_error_led, OUTPUT);
}

void loop()
{
  unsigned int connection_status = modbus_update(packets);
  
  if (connection_status != TOTAL_NO_OF_PACKETS)
  {
    digitalWrite(connection_error_led, HIGH);
    // You could re-enable the connection by:
    //packets[connection_status].connection = true;
  }
  else
    digitalWrite(connection_error_led, LOW);
  
  // update the array with the counter data
  //regs[3] = packet1->requests;
  //regs[4] = packet1->successful_requests;
  //regs[5] = packet1->total_errors;
/*  regs[6] = packet2->requests;
  regs[7] = packet2->successful_requests;
  regs[8] = packet2->total_errors; */
  Serial.print("Requests: ");
  Serial.println(regs[3]);
  Serial.print("Successful: ");
  Serial.println(regs[4]);
  Serial.print("Errors: ");
  Serial.println(regs[5]);
    Serial.print("data 1: ");
  Serial.println(regs[0]);
  Serial.print("data 2: ");
  Serial.println(regs[1]);
   Serial.print("data 3: ");
  Serial.println(regs[2]);
  Serial.print("data 4: ");
  Serial.println(regs[6]);
  Serial.println(regs[7]);
  Serial.println(regs[8]);
  Serial.println(regs[9]);
  Serial.println(regs[0]);
  long temp = (long)regs[1] << 16 | regs[0];
  Serial.println(temp);
  //long temp1 = (long)temp >> 4;
  //Serial.println(temp1);  
  delay(1000);
}

and here is the output i get with the above code…

Requests: 0
Successful: 0
Errors: 0
data 1: 0
data 2: 31376
data 3: 0
data 4: 0
0
0
769
0
2056257536

Again thanks for your time and help

a)
ok, what I see now is you are using “NG” Version:
https://github.com/angeloc/simplemodbusng

not the original SimpleModbusMaster version from Juan
https://drive.google.com/drive/folders/0B0B286tJkafVYnBhNGo4N3poQ2c

correct? Is there any reason why you have choosen the NG (last maintained 6 years ago) vs. the original (4 years ago)

b) you can uncomment regs[3]-regs[6] again, I see it’s based on the example

c) do you get any serious values from your device?
my guess what might be a problem is the delay 1000 at the end of the code.

this might blocks the modbus_update(packets) … the finite state machine might not be called.

What you should do is following: let the loop call modbus_update as often as possible. don’t block your loop with a delay.
put the serial output in once a second loop (like the BlinkWithoutDelay example)

define

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 1000; // constants won't change:

and replace your output with something like this (untested)

unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
  previousMillis = currentMillis;
  for (byte i = 0; i<9;i++)
  {
    Serial.print(F("reg "));Serial.print(i);
    Serial.print(F("="));Serial.println(reg[i]);
  }  
}

if this doesn’t help, consider to switch to Juans original (and newer) Version. It’s slightly different.

Hi:

to tell you the truth i don’t quite remember why I used the first library over the second one…

but i tried Juan Bester’s Library with this example code:

#include <SimpleModbusMaster.h>

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 4 

#define LED 13

// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 2

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 141, 2, 0);
  //modbus_construct(&packets[PACKET2], 1, PRESET_MULTIPLE_REGISTERS, 1, 1, 0);
  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial, baud, SERIAL_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
  pinMode(LED, OUTPUT);
}

void loop()
{
  modbus_update();
  
  //regs[0] = analogRead(0); // update data to be written to arduino slave
  
  //analogWrite(LED, regs[0]>>2); // constrain adc value from the arduino slave to 255
  Serial.print("Reg 0 ");
  Serial.println(regs[0]);
  Serial.print("Reg 1 ");
  Serial.println(regs[1]);
  long temp = (long)regs[1] << 16 | regs[0];
  Serial.print("Combined Long: ");
  Serial.println(temp);
  long temp1 = (long)regs[0] << 16 | regs[1];
  Serial.print("Combined Long1: ");
  Serial.println(temp1);
}

and i get this result…

Reg 0 0
Reg 1 22095
Combined Long: 1448017920
Combined Long1: 22095

as for the first code i was using with the NG library

#include <SimpleModbusMaster.h>

// led to indicate that a communication error is present
#define connection_error_led 13

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 500 // the scan rate

// If the packets internal retry register matches
// the set retry count then communication is stopped
// on that packet. To re-enable the packet you must
// set the "connection" variable to true.
#define retry_count 10 

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 4 

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  //PACKET2,
  // leave this last entry
  TOTAL_NO_OF_PACKETS
};

// Create an array of Packets for modbus_update()
Packet packets[TOTAL_NO_OF_PACKETS];

// Create a packetPointer to access each packet
// individually. This is not required you can access
// the array explicitly. E.g. packets[PACKET1].id = 2;
// This does become tedious though...
packetPointer packet1 = &packets[PACKET1];
//packetPointer packet2 = &packets[PACKET2];

// The data from the PLC will be stored
// in the regs array
unsigned int regs[9];

void setup()
{
  // read 3 registers starting at address 0
  packet1->id = 1;
  packet1->function = READ_HOLDING_REGISTERS;
  packet1->address = 141;
  packet1->no_of_registers = 2;
  packet1->register_array = regs;
  
 /* // write the 9 registers to the PLC starting at address 3
  packet2->id = 2;
  packet2->function = PRESET_MULTIPLE_REGISTERS;
  packet2->address = 3;
  packet2->no_of_registers = 9;
  packet2->register_array = regs;*/
  
  // P.S. the register_array entries above can be different arrays
  
  // Initialize communication settings etc...
  modbus_configure(baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
  
  pinMode(connection_error_led, OUTPUT);
}

void loop()
{
  unsigned int connection_status = modbus_update(packets);
  
  if (connection_status != TOTAL_NO_OF_PACKETS)
  {
    digitalWrite(connection_error_led, HIGH);
    // You could re-enable the connection by:
    //packets[connection_status].connection = true;
  }
  else
    digitalWrite(connection_error_led, LOW);
  
  // update the array with the counter data
  regs[3] = packet1->requests;
  regs[4] = packet1->successful_requests;
  regs[5] = packet1->total_errors;
  /*regs[6] = packet2->requests;
  regs[7] = packet2->successful_requests;
  regs[8] = packet2->total_errors;*/
  Serial.print("Requests: ");
  Serial.println(regs[3]);
  Serial.print("Successful: ");
  Serial.println(regs[4]);
  Serial.print("Errors: ");
  Serial.println(regs[5]);
    Serial.print("data 1: ");
  Serial.println(regs[0]);
  Serial.print("data 2: ");
  Serial.println(regs[1]);
   Serial.print("data 3: ");
  Serial.println(regs[2]);
  Serial.print("data 4: ");
  Serial.println(regs[6]);
  Serial.println(regs[7]);
  Serial.println(regs[8]);
  Serial.println(regs[9]);
  Serial.println(regs[0]);
  long temp = (long)regs[1] << 16 | regs[0];
  Serial.println(temp);
  //long temp1 = (long)temp >> 4;
  //Serial.println(temp1);  
  //delay(1000);
  modbus_update(packet1);
}

i get this result…

Requests: 11
Successful: 1
Errors: 10
data 1: 0
data 2: 22095
data 3: 0
data 4: 0
0
0
769
0
1448017920

i had to change the register i’m getting info from to total flow this month, since I have not logged anything today into the flowmeter…

but the data in the first and second registers in consistant using both libraries so i must asume that that is the data stored in the register… somehow encoded… in hex or something… the data according to the flowmeter lcd should be around 8.39 cubic meters…

thanks!!!

Hi… I’m finally getting some data that i know is right and that i can make sense of…

with this code… (i modified the code to use and arduino mega to have to serial communication ports just to make sure this was not getting in the way… the results were the same…)

#include <SimpleModbusMaster.h>

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1 
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 4 

#define LED 13

// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 2

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  //PACKET2,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];
int incomingByte = 0;   // for incoming serial data

void setup()
{
  
  Serial.begin(9600);
  Serial1.begin(9600);
  // Initialize each packet
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 105, 2, 0);
  //modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS, 1, 145, 0);
  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial1, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
  pinMode(LED, OUTPUT);
}

void loop()
{
   //int incomingByte = 0;   // for incoming serial data
  modbus_update();
   if (Serial1.available()) {
                // read the incoming byte:
                int incomingByte = Serial1.read();
   }
  //regs[0] = analogRead(0); // update data to be written to arduino slave
  
  //analogWrite(LED, regs[0]>>2); // constrain adc value from the arduino slave to 255
  Serial.print("Reg 0: ");
  Serial.println(regs[0]);
  Serial.print("Reg 1: ");
  Serial.println(regs[1]);
   Serial.print("Reg 2: ");
  Serial.println(regs[2]);
  Serial.print("Reg 3: ");
  Serial.println(regs[3]);
   Serial.print("Reg 4: ");
  Serial.println(regs[4]);
  Serial.print("Reg 5: ");
  Serial.println(regs[5]);
   Serial.print("Reg 6: ");
  Serial.println(regs[6]);
  Serial.print("Reg 7: ");
  Serial.println(regs[7]);
  Serial.print("Reg 8: ");
  Serial.println(regs[8]);
  Serial.print("Reg 9: ");
  Serial.println(regs[9]);
  long temp = (long)regs[1] << 16 | regs[0];
  Serial.print("Combined Long: ");
  Serial.println(temp, DEC);
  long temp2 = (long)regs[3] << 16 | regs[1];
  Serial.print("Combined Long2: ");
  Serial.println(temp2, DEC);
  long temp1 = (long)regs[9] << 16 | regs[0];
  Serial.print("Combined Long1: ");
  Serial.println(temp1, DEC);
  Serial.println(incomingByte, HEX);
  
  
}

I retreive the 105-106 register data that accorgin to the manual is the total times the unit has been turned on and off… If i go to the device and look at this information it gives me the number 415…

and from the code i get this results

Reg 0: 57
Reg 1: 415
Reg 2: 516
Reg 3: 0
Reg 4: 1000
Reg 5: 0
Reg 6: 0
Reg 7: 0
Reg 8: 205
Reg 9: 204
Combined Long: 27197497
Combined Long2: 415
Combined Long1: 13369401
0

so i get the actual number in reg [1]…

it’s the first time i get some data i know it’s right… still have to make sense of the other data i’m getting…

because once i try to get data out of the 141 register i get this…

Reg 0: 0
Reg 1: 22880
Reg 2: 516
Reg 3: 0
Reg 4: 1000
Reg 5: 0
Reg 6: 0
Reg 7: 0
Reg 8: 205
Reg 9: 204
Combined Long: 1499463680
Combined Long2: 22880
Combined Long1: 13369344
0

thanks for any help…

Hello,

I'm planning to do a project to read water flow meter. Not sure which way to go, but ultrasonic sensors seems good and already have a look at tuf2000m.

Have you got it running?

I also read data from tuf2000m. the data is real or floating point format .please see IEEE754

Hi estuardomunoz, I’m working with the same device for the same application, thank you for telling me the Arduino Mega Pinout used