Water level sensor QDY30A modbus RS485 with ESP32 S2 mini

Did you look at my GitHub GitHub - PhillyGilly/Rainwater-Harvesting-Management

Just came across this by chance, I have several of these sensors used to monitor levels in water tanks. If anyone’s still got questions (I see that most are from 2024) let me know, otherwise some general notes based on stuff asked earlier:

  • To check them, use an RS485 adapter or modbus gateway connected to PC software like ComTestPro. This means you can check functioning without having to guess whether you’ve got pins swapped or a million other things that can go wrong.
  • For a quick go/no go test, read holding register 0 (function 0x03) which returns the device address, 1. If you’re doing things the hard way then the bits on the wire will be 01 03 00 00 00 01 → 01 03 02 00 01.
  • The baud rate and modbus address are changeable, and unlike many other devices don’t need a power cycle but take effect immediately. So when you change the ID from the default 1 that everything uses to (say) 50, the very next message will have to go to ID = 50 otherwise you won’t get a response any more.
  • To get a reading in litres for whatever you’re measuring, in my case 2000L and 4000L tanks, fill it to the overflow mark and then calculate the scaling factor to go from mm/cm to litres.
  • The sensor senses the pressure differential between atmospheric and liquid, so the atmospheric pressure is sampled through a foam plug in a small tube heatshrunk into the cable. If you’re making the connection inside a sealed waterproof enclosure you need to cut away the heatshrink and feed the tube through a hole outside the enclosure to sample atmospheric pressure, not whatever’s inside the enclosure.

And now a question, they’re advertised as working at 24V, has anyone run them reliably at 12V? Some sellers claim 12V-24V which would save having to wire in a boost converter that I’d forgotten I need.

One comment on that, the photos show the use of LED power supplies… those things typically don’t output a DC voltage like a standard power supply but high-frequency switching noise that relies on the LED lights to act as rectifier and smoothing capacitor-equivalent. If you’ve got access to a scope, have a look at the output waveform, but more generally never use anything with a name like “LED electronic transformer” or similar as a general-purpose power supply. You may get lucky and get one that’s a proper power supply, but a lot of them output what can only be called “crap” instead of the rated DC voltage. For example I once had to diagnose an apparently DOA power supply that wasn’t producing any output, after lots of faffing around switched to AC and it produced a reading (just not the rated one), and then finally on a scope I saw it was outputting nothing but switching noise, no DC component at all. Worked fine driving LED downlights but not anything else.

1 Like

And some give 12-36V, some 10-30V. I would expect the RS485 version to work at 12V, maybe even lower.
0-10V version might need >12V.

Yeah, it just seems like a weird voltage, I’d expect 12V default and 24V special order. Most modbus devices are 9-36V, given the fixed voltage I’m guessing they’re using a linear regulator to avoid having switching noise present and/or because they need a stable reference for the comparator or ADC used to read the sensor voltage, but that’s just random guesswork.

If I get time over the weekend I’ll try it at various voltages between 12 and 24V. I’m sort of expecting to get erroneous readings at lower voltages, but let’s see how it goes.

Tried it at 12V, you can read values but you can’t write anything, so attempting to change the ID from 1 to, say, 10 fails with the response indicating the ID is still 1. I’ve seen this behaviour on devices with insufficient voltage present to rewrite the config EEPROM, will need to rewire it to a bench supply to test > 12V.

Update: This is a bit more complex than that, while it’s superficially the same as the standard S-YW-01B, there are some fundamental differences that mean you can’t interchange the code used to control them. I’ll report back when I’ve done some more experimentation.

OK, figured it out. The QDY30A, despite being physically identical and mostly identical at the read-only modbus level, is not the same as the more common S-YW-01B. The most obvious difference is the units identifier, the non-QDY is purely a pressure sensor and has a units scale of 1-6, the QDY firmware accommodates all sorts of other measurements and instead of overlapping them with the non-QDY values they inserted new ones at the start. As a result where the non-QDY uses 0x01 to denote cm of water, the QDY uses 0x11, with the range going up to 0x17.

The other thing is that the QDY doesn’t store its settings in nonvolatile memory like every other modbus device I’ve worked with, there’s a separate command to save the settings and if you don’t issue that they’ll reset when power is removed. This is why I thought the write wasn’t being done, it’s not due to the voltage being too low to program the EEPROM but because you need to issue two commands to update the settings, not one.

Here’s an example, to set the modbus ID/address to 10 and the baud rate to 19200.

Write reg 16, ID = 1 -> 1				(Factory Reset)
Write reg 0, ID = 1 -> 10 / 0x0A		(Write Device Address = 10)
Read reg 0, ID = 10 -> 10 / 0x0A		(Read Device Address = 10)
Read reg 1, ID = 10 -> 3				(Read Baud Rate, 3 = 9600)
Write reg 1, ID = 10 -> 4				(Set Baud Rate, 4 = 19200)
[Change host baud rate to 19200]
Read reg 0, ID = 10 -> 10 / 0x0A		(Read Device Address = 10)
Write reg 15, ID = 10 -> 0				(Save Settings)
[Power off, power on again]
Read reg 0, ID = 10 -> 10 / 0x0A		(Read Device Address = 10)

This uses a device address read at each step to confirm that things are OK. So once you’ve made any config changes you need to write 0 to register 15 to save them, otherwise they’ll be reset when power is lost.

Apart from that it works fine at 12V, verified with various water levels where it’s accurate to a mm as far as I can see. However see above, just because the QDY is OK with 12V doesn’t necessarily mean that non-QDY devices will be OK too.

1 Like

wanted to share my working code
using the QDY30A-B Hydrostatic water Pressure Level Sensor probe (1m) RS485 along with one of these RS485 to UART converters. A/B did not need to be swapped in my case. using a 12 volt power supply for the sensor probe.


# ================= RS-485 / Modbus (QDY30A-B) =================
uart:
  id: rs485_uart
  tx_pin: GPIO13
  rx_pin: GPIO14
  baud_rate: 9600
  parity: NONE
  stop_bits: 1

modbus:
  id: rs485_bus
  uart_id: rs485_uart

modbus_controller:
  - id: qdy30
    address: 0x01
    modbus_id: rs485_bus
    setup_priority: -10
    update_interval: 3s

# --- Sensors ---
sensor:
  - platform: modbus_controller
    modbus_controller_id: qdy30
    name: "QDY30 Unit Code"
    id: qdy_unit_code
    address: 0x0002
    register_type: holding
    value_type: U_WORD
    accuracy_decimals: 0
    entity_category: diagnostic

  - platform: modbus_controller
    modbus_controller_id: qdy30
    name: "Water Level (mm)"
    id: level_mm
    address: 0x0004
    register_type: holding
    value_type: U_WORD
    unit_of_measurement: "mm"
    device_class: distance
    state_class: measurement
    accuracy_decimals: 0
    filters:
      - sliding_window_moving_average:
          window_size: 5
          send_every: 1
      - lambda: |-
          if (isnan(x) || x <= 0) return {};
          return x;

  - platform: template
    name: "Water Level (m)"
    id: level_m
    unit_of_measurement: "m"
    device_class: distance
    state_class: measurement
    accuracy_decimals: 3
    update_interval: 3s
    lambda: |-
      if (isnan(id(level_mm).state) || id(level_mm).state <= 0) return {};
      return id(level_mm).state / 1000.0f;

  - platform: template
    name: "Water Level"
    id: level_pct
    unit_of_measurement: "%"
    state_class: measurement
    accuracy_decimals: 1
    update_interval: 3s
    lambda: |-
      if (isnan(id(level_m).state) || id(level_m).state <= 0) return {};
      const float FS_M = 1.0f;
      return clamp((id(level_m).state / FS_M) * 100.0f, 0.0f, 100.0f);

# Human-readable unit string
text_sensor:
  - platform: template
    name: "QDY30 Units"
    id: qdy_units
    update_interval: 5s
    lambda: |-
      if (isnan(id(qdy_unit_code).state)) return {};
      const int code = static_cast<int>(id(qdy_unit_code).state);
      switch (code) {
        case 0x11: return {std::string("cm of water")};
        case 0x12: return {std::string("m of water")};
        case 0x13: return {std::string("kPa")};
        case 0x14: return {std::string("Bar")};
        case 0x15: return {std::string("PSI")};
        case 0x16: return {std::string("Temperature (°C)")};
        case 0x17: return {std::string("Custom/Other")};
        default:   return {std::string("Unrecognized")};
      }

thanks for your help

What’s the accuracy of this sensor? Looking for something that is accurate between 0 and 20cm of water. Preferably with an accuracy < 1cm

Iv’ve got mine working, and it outputs mm values. Allowing for the position of the sensor in the case, it seems accurate enough. I haven’t measured the actual accuracy though. The changes it outputs are in single-digit milimeters, and given the sensor isn’t moved, it seems stable for several hours.

Given you asking for sub-cm precision, I should warn you that the sensor head is quite hefty. It’s more than 25mm thick and weighs several hundred grams. Setups requiring millimeter precision tend towards the delicate, so factor that in.

Does anyone have an actual datasheet for the actual QDY30A? I couldn’t find one online. Running a device without knowing how it actually is supposed to be uset makes me nervous…

I’ve looked a bit deper into the question of precision, and can report that the sensor is definitely capable of detecting sub-cm changes in water level.

On the other hand, I’ve also found out that the presence or absence of an air bubble in the membrane cavity can change the reading by up to 15mm.

There are several explanations for that effect:

If we assume the actual sensor is some 15mm above the ingress openings, assuming a vertical orientation of the sensor, the effective water colum WOULD actually shorten by that amount if the air bubble is removed.

On the other hand, there is the Laplace-effect, in which the radius of the air-water-interface causes a rise in pressure which could change the reading by more than 10mm, given a roundabout 1mm radius of the air/water interface.

If your setup allows for a horizontal mounting of the sensor so the air can escape, that would not be a problem.

If on the other hand, your sensor has to be mounted in a vertical orientation and also is expected to fall dry now and then, it could significantly impact measurements of the precision you sem to need.

Thats awesome info, thanks a million!

I have ordered two sensors and hope to do some testing this Nov. I’ll update this thread with my findings…

It was available from somewhere since I have a printout of it bound into a manual, but I can’t remember where I got it from. If you’ve got any specific questions I can look up the details.

I went a bit further down the rabbit hole, and here’s what I found: Dumping the air out of the measurement chamber vestibule bubble by bubble caused a lowering of the level by just four millimeters, one millimeter at a time, from 203mm to 199mm. Dumping the single remaining bubble out made the measurement jump back suddenly by 13mm to 186mm. Those measurements were fairly repeatable over several attempts. Unless we are prepared to accept that there exists a narrow, 13mm long channel inside the sensor, which always dumps out in a single bubble, I think the Laplace hypothesis is the more credible one.

Interestingly, the measurement WITH air in the chamber seems to be closer to the true value than the measurement with the air dumped out, so we have to assume the sensor logic compensates for that effect.

I don’t know how helpful that will be to you, but now you know.

Hello, I would like to ask for help.

I also have a QDY30A sensor in the Modbus version. I use MAX485 with automatic receive/transmit mode switching (i.e., no flow pin). After purchasing and connecting it, everything worked perfectly for six months. Now I have a problem where the sensor reads correctly for a moment, but most of the time the transmission is faulty.

I tried buying a new MAX485 converter and the situation is the same.
Where should I look for the error?

Thank you.

Have you tried this wizard to assist with troubleshooting?

1 Like