Reading a TUF-2000M Ultrasonic Flow Meter with an Arduino or ESP8266

I had a flood in the garage the other day and realized how great of an investment my flood sensor had been, saving me literally weeks of time and thousands of dollars in repairs. As I considered buying more flood sensors to cover more parts of the house, the thought to put a flow meter on the main water inlet to the house popped into my mind. It’s not quite as clear of a signal as a flood sensor, but if I detect flow when everyone is asleep or when on vacation, I can be sure that something is going wrong and have Home Assistant give me an alert.

I didn’t want to cut into my water main and put a in-line flow meter in, so when I saw a reasonably priced clamp-on ultrasonic flow meter called the TUF-2000M, I had to bite.

The TUF-2000M Ultrasonic Clamp-on Flow meter

I also purchased a water main valve actuator to turn off the water automatically but it was not strong enough to actuate my valve so I sadly had to return it.

The kit comes with a lot of wire and a power cord. You have to strip them yourself and hook them up. BTW, the power transformer is totally crap and broke almost immediately in a kind of scary way.

Getting the RS-485 data into a computer

At first I tried decoding the RS-485 directly with my FT232H chip from Adafruit. I could not get it working. Eventually I bought a RS-485 to TTL converter to put between the FT232H and the sensor. This worked quickly much easier. For settings, I have my MODBUS interface mode (menu 63) set to RTU only, my serial interface (menu 62) set to 9600,None, 0, 1, and my data logger option turned off.

The RS-485-TTL converter hooked to a FT232H, which is plugged into the USB port of a computer

Having cracked the physical layer and brought RS-485 down to 3.3V, I was able to decode the data packets. This sensor uses the traditional industrial protocol, MODBUS. With Python, you can operate this with minimalmodbus.

Registers appear (at first) to be 1-based in this system. For example, reading register 99-100 from the manual for a float for Reynolds number gives:

Sounds about right. Upon trying to write things, I ran into trouble. Writing ints to register 60 is supposed to take you to a window.

But strangely, writing to register 59 causes the device menu to change as expected!

Clearly something is wrong though because if I try to go to M25 in this way, it goes to M20. Aha! After fiddling it turns out that you have to use hex values! So to get to window M25, you use

Register 158 is supposed to be the current window. The proper value may be obtained from register 157 in this case, again suggesting a 0-based numbering of the registers

To make sure I can read floats right, I went to M06 which shows T1 and T2 temperatures, both around 46.8 °C (dummy values because the probes aren’t in). I should be able to read these from registers 33-34 and 35-36, according to the 1-based manual. We try reading from 1-based 33 and 35 first:

[Scratches head]. It works! Is it possible that writing registers is 0-based and reading them is 1-based!?

I’m just going to have to get some flow going and read the value because this seems like hit or miss.

Interacting with the TUF-2000M from a ESP8266

Given TTL serial output from the converter, you can read the 3.3V TTL values into a ESP8266 or Arduino or Raspberry Pi for transmitting to something that can graph and respond. On an Arduino-like device, the Software Serial library is the key. It allows you to use any available pair of digital GPIO pins (hint: don’t use the ones used by the hardware serial interface) for reading serial data. On a Raspberry Pi, you can configure GPIO to do serial like this.

This library seemed like the best one for a ESP8266 at first so I tried it first. We will do a verification test before hooking up flow by ensuring we can read on of the 46 °C temperatures from register 33. If we can, then we sure as heck will be able to read a flow rate from register 1.

From the debug mode above, the response to a get_float call was:

That’s address, register, num bytes (4), 4 bytes of data, and a checksum.

To make sure I can interpret the data payload, I did some Python:

Sweet. Note that the single precision float from the flow meter is Big Endian. Ok off to the simpler microcontroller library!

After fiddling around a bit with endianness, I was able to get this result reading register 33 from the ESP8266/NodeMCU:

Yesss! So we, my friends, are in business! Switching the register of interest back to 1 for the flow rate, here is the test program in its entirety:

For the final version, I will add in WiFi, AndroidOTA (for over-the-air updates), and MQTT for sending the data back to my Home Assistant server for monitoring. OMG this is totally going to work! 🙂 🙂 🙂

Configuring the TUF-2000M for my water pipe and mounting

Once I figured out the menus, I entered my pipe OD, as measured by a micrometer. It’s 35.5 mm. Then I guestimated the pipe thickness from standard pipes as 3.175 mm. Water was the default fluid. I set the pipe material to Copper. I set the transducer type to “19. Clamp-on TS-2” since I got the small clamps. Then I went over to M21 and read out 10.8219 mm for my transducer spacing. OK. Time to mount.

Oh shoot you’re supposed to use some kind of coupling grease between ultrasonic transducers and the pipe. Hmm maybe I’ll just try this bearing grease I have sitting around.

Checking the signal strength on the sensor, I was able to get 85% signal with Q also around 85%, which seems pretty good to me. So the bearing grease is working? Nice. I also rotated them to be 45° rather than straight up and down as indicated in the manual.

Here are the numbers in Home Assistant! It definitely works. I messed around with the zero-cutoff value to try to reduce the low-flow noise. This thing is not going to meaningfully detect a trickle out of a faucet. But it certainly can tell you when the faucet is running full blast. I am not sure what’s up with the oscillatory behavior after the shower at 7:30am. I’m pretty sure flow isn’t going backwards to the city.

Water flow meter readings overnight, with a shower at the end. Hmm the negative flow is a bit wonky. Oh and the units are wrong. That should say m³/hr, not gal/hr.

BTW don’t use abs on a float in Arduino. See this issue.

This is how long it took me to realize abs doesn’t work on floats in Arduino, leading to a ESP8266 crash. Use fabs.

For more reference, here’s another picture of the connections ( in lieu of a proper wiring diagram).

And one more

System mounted in prototype configuration

7 thoughts on “Reading a TUF-2000M Ultrasonic Flow Meter with an Arduino or ESP8266”

  1. Hi Sir,

    May i get the connection diagram for how u connected from ”TUF 2000M” to “RS485 to TTL Converter” to the Arduino. It will be very helpful for me. If can please send it to my email address

    Thank you.

    1. I added a slightly better pic to the bottom of the post in lieu of a wiring diagram. Connection from the TTL to arduino is just to +/- and two arbitrary GPIO ports that you’ll use in the SoftwareSerial library.

  2. Super project, an think of ding something similar for the drainage gates in one of my fields,

    The code you added to your article should help speed things up as am quite basic when it come to coding/electronics.

    Many thanks

  3. Cool project. I did something similar with the TUF2000m to measure heat flow from a heat pump.
    I also have random oscillation between +- 0.15 m3/h in case the water is still in a 28mm copper pipe.
    It gets much more stable with the W- method but the Q factor often falls below 50. Maybe better coupling would help. I also use bearing greese. Have tried a lot of other pastes but this gave the most stable reading so far.

  4. Thanks for sharing this.
    Where did you get the communication specification for TUF-200M?
    I am planning to buy them, but the factory told my contact person in China that he doesn’t have the protocol documentation for RS-485.

  5. Awesome work!
    I wanted to do this for a while and thanks to your post I managed to talk to the thing! The data I’m getting isn’t quite right yet, but I think I’m on the right track. If you could share the complete code with the MQTT bit, I would greatly appreciate it. I don’t have experience with that protocol and it would be really helpful to know which way you went.


Leave a Reply

Your email address will not be published. Required fields are marked *