ESP Pressure Measuring Device

by Al1000 in Circuits > Electronics

584 Views, 11 Favorites, 0 Comments

ESP Pressure Measuring Device

30.png

After installing an automatic irrigation system this summer and connecting it to Home-Assistant, some unexpected issues popped up. These needed to be solved, and that’s how this weekend project came to life.

Unfortunately, problems kept occurring that prevented the irrigation from working as reliably as I had envisioned. Since the sprinklers run at 5 AM in the morning, you don’t necessarily notice when something goes wrong - especially when you’re on vacation.

I was able to identify the following problems:

  1. Pump shuts down when a leak is detected - e.g., a dripping water hose
  2. Pump pre-filter gets clogged - making it impossible to prime or resulting in insufficient water pressure for the sprinklers
  3. Disc filter gets clogged - again resulting in insufficient water pressure for the sprinklers

Since I had installed a Rain Bird disc filter with 2 additional outlets for pressure sensors, I decided it was time to actually put them to use.

My solution is to monitor the pressure before and after the filter. This way, if there’s a pump malfunction or a clogged pre-filter, insufficient pressure can be detected at the inlet. If the inlet pressure is fine but the outlet pressure is too low, then the disc filter is clogged.

In both cases, I could get alerts through Home-Assistant. If the pump is malfunctioning, the outlet could even be automatically switched on and off to clear the fault.

Supplies

Materials:

  1. 1 x Perfboard 60 x 40mm
  2. 1 x D1 Mini ESP8266
  3. 2 x Pressure sensor WNK83MA 5V 0-10 Bar, G1/4” male thread
  4. 1 x Analog-to-Digital Converter ADS1115 i2c
  5. 1 x Mini power supply 220V AC -> 5V DC
  6. 2 x Pin header 8-pin
  7. 1 x Pin header 10-pin
  8. 1 x Connector 4-pin (optional)
  9. 2 x Angle coupling G1/4” female thread both sides
  10. 1 x Rain Bird disc filter
  11. 1 x OLED breakout board 0.96” (optional)
  12. 4 x Wood screws M2 x 8mm

Tools:

  1. Soldering iron
  2. Solder
  3. Hot glue gun
  4. Wires
  5. Wire stripper
  6. Side cutters
  7. Teflon tape
  8. Pump pliers
  9. 6mm drill bit

Solution Design

For mounting on the water filter, the solution was pretty much given. A water-compatible pressure sensor with G1/4 threading. Plus two angle fittings so the sensors can be mounted sideways.

The sensors deliver 0.5-4.5V voltage, which can be measured via the ESP. While some ESP32s do have two AD inputs, the second input can only be used when WiFi is turned off :-(

So I needed an external ADC. The ADS1115 has 4 analog inputs and i2c bus, making it easy to connect. It works with either 3.3V or 5V. But the pressure sensor is designed for 5V. To avoid needing a level converter, I decided to go with 3.3V power supply for the ADS1115 so it can be connected directly to the ESP.

However, the maximum input level for the ADC is VDD+0.3V - so 3.6V in this case. But the pressure sensor delivers up to 4.5V at 10 bar. My pump will never generate 10 bar pressure though - more like 4.5 bar. With a bit of safety margin, I’ll assume 5 bar max, where the sensor should deliver about 2.25V, so that should work :-)

Since the ADC only uses 2 pins, many microcontrollers are suitable: ESP32, ESP8266, ESP01, ESP12F, etc.I found a D1 Mini with ESP8266 in my tinkering box - that should do the trick. For the base software, I wanted to use ESPHome. The ADS1115 is also directly supported by ESPHome. For power supply, I found a mini power adapter in my parts bin. 5V and 600mA should be plenty.

ESPHome Software Installation

I started with a basic installation via USB and only made minimal configuration changes.I’ll show the complete code with all the extensions at the end.

esphome:
name: irrigation-pressure
friendly_name: Irrigation-Pressure

esp8266:
board: esp01_1m

# Enable logging
logger:
level: WARN

# Status LED
status_led:
pin:
number: GPIO02
inverted: true
# Enable Home Assistant API
api:
encryption:
key: "********"

ota:
- platform: esphome
password: "******"
- platform: web_server # Add this for web-based OTA uploads

web_server:
port: 80
version: 3

wifi:
ssid: !secret wifi_ssid_iot
password: !secret wifi_password_iot

# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Irrigation-Pressure"
password: "******"

captive_portal:

button:
- platform: restart
icon: mdi:power-cycle
name: "ESP Reboot"

sensor:
- platform: wifi_signal
name: "WiFi Signal"
entity_category: "diagnostic"
update_interval: 60s

text_sensor:
- platform: version
name: "ESPHome version"


The initial base software was transferred using esptool.py. (Download: https://github.com/espressif/esptool)

esptool.py write_flash 0x0 Downloads/irrigation-pressure.bin


Quick test with the browser at http://irrigation-pressure.local

Works!

Breadboard Setup

Pressure-Measurement-Sketch.png
1.png
2.png

The first test setup was quickly put together. For the ADC, I connected the ADDR pin to GND to get a stable bus address of 0x48.

Connecting the pressure sensors was straightforward. 5V (red), GND (black) and the output (yellow) to the ADC. The program was extended to integrate the ADC and display the readings from the web interface.

i2c:
sda: GPIO04 # D2
scl: GPIO05 # D1
scan: true

ads1115:
- address: 0x48 # ADDR -> GND

sensor:
# Sensor channel 0
- platform: ads1115
id: ch0
multiplexer: 'A0_GND'
gain: 4.096
update_interval: 1s
name: "Channel 0"
unit_of_measurement: "V"
accuracy_decimals: 2

# Sensor channel 1
- platform: ads1115
id: ch1
multiplexer: 'A1_GND'
gain: 4.096
update_interval: 1s
name: "Channel 1"
unit_of_measurement: "V"
accuracy_decimals: 2

...


For the first test, I output the measurement directly in volts. We’ll get to calibration and conversion to bar later. This time I was able to upload the new software directly via OTA.

The sensor readings are around 0.499V at rest, which corresponds to the ambient pressure of about 0.9 bar. I tried a function test by blowing into the sensors, but I couldn’t achieve more than a 0.05V :-)

Knowing the difference between the two sensors would also be handy. So I quickly extended the code:

sensor:
...
# calculate difference between channel 0 and channel 1
- platform: template
name: "Pressure Difference"
id: difference
accuracy_decimals: 2
lambda: 'return id(ch1).state - id(ch0).state;'
...

OLED Display (Optional)

3.png

Now I could have moved on to developing the PCB, but there was still one problem. The D1 Mini already had pin headers soldered on top. Unfortunately, I didn’t have another D1 Mini without pre-soldered pin headers. I didn’t want to desolder them, and leaving them unused was absolutely out of the question!

So I rummaged around in my parts bin and what do you know: an OLED breakout board that fits perfectly on the D1 Mini.

The OLED display is also controlled via i2c. Luckily, I had used the same pins that the breakout board uses, so I just needed to plug it in! No additional wiring required!

Now just a quick software adjustment to bring the OLED display to life:

font:
- file: "gfonts://Roboto"
id: small
size: 13

display:
- platform: ssd1306_i2c
model: "SSD1306 64X48"
address: 0x3C
lambda: |-
it.printf(2, 0, id(small), "0: %.2f V", id(ch0).state);
it.printf(2, 16 , id(small), "1: %.2f V", id(ch1).state);
it.triangle(2, 45, 7, 36, 12, 45);
it.printf(20, 33, id(small), "%.2f V", id(difference).state);


Transfer to Perfboard

4.png
5.png
6.png
7.png
8.png
9.png

Alright, now the PCB needs to be created. Since I only need one unit, I skipped developing a proper PCB and just used a perfboard instead.

In order to be able to operate it directly from the power outlet, I also prepared the power supply unit.

Enclosure

10.png
12.png
13.png
15.png
17.png

Of course we still need a housing. So I created and printed a suitable enclosure using Fusion360.

You can find the STL file here: https://www.thingiverse.com/thing:715617 You might need to adjust the lid if you position the OLED display or D1 Mini differently on your board.

If the screws don’t fit, a hot glue gun works wonders :-)

Preparing the Pressure Sensor Cables

14.png

To protect the cables to the sensors, I ran them through corrugated tubing with a 20mm diameter and screwed on the connector.

Installing the Sensors

18.png
19.png
20.png
21.png
22.png
23.png
24.png
25.png
26.png
27.png
28.png
29.png

Next up is the filter. For this, you need to unscrew the cap and remove the filter so no drilling debris stays in the filter. I drilled two 6mm holes each and deburred them with a step drill bit.

I gave the housing a good rinse and reassembled it. As you can see, the sensor connections are located on the back of the housing. Also, both the connections on the filter and the pressure sensors have external threads. That’s why you need a coupling with 2 x internal threads. I went with 90-degree angles so the sensors can be mounted sideways.

To make sure the threaded connection between the pressure sensors, angle coupling, and housing is watertight, I sealed the threads with teflon tape. 3-4 wraps should do it. Make sure to wrap the teflon tape from the right direction so it tightens when screwing in. By the way, hemp isn’t an alternative here for copper/plastic connections. Hemp swells up and can damage the plastic.

Now install it back into the water supply system, plug in the connector, and you're done!

Test and Calibration

messreihe.png
Kurve.png
calibr.Table.png

After switching on the pump, the water pressure rose and so did the display. But what good is a reading in volts?! I needed to convert it to bar.

As a reference, I used the display on my garden pump (Gardena 4000/4 LCD) and set up a measurement series. I opened different irrigation circuits and noted the OLED display reading in volts and the bar reading on the pump.

I entered the results into a spreadsheet and connected the measurement points. Additionally, I added a linear trend line and had it determine the formula. You can’t complain about the linearity of these pressure sensors.

Using the calculated slope of the trend line, I created a calibration table that outputs the appropriate voltage for even pressure values.

These now need to be used in the code. Fortunately, ESPHome has linear interpolation built in. I also adjusted the metadata of the sensor definitions so they display nicely in Home-Assistant.

Since the values are quite jittery, I smoothed them using the median function and used the delta value to ensure changes are only transmitted to Home-Assistant when the value changes by at least 1%.

sensor:
# Sensor channel 0
- platform: ads1115
id: ch0
multiplexer: 'A0_GND'
gain: 4.096
update_interval: 1s
name: "Channel 0"
unit_of_measurement: "bar"
icon: "mdi:barometer"
device_class: "pressure"
state_class: "measurement"
accuracy_decimals: 2
filters:
- calibrate_linear:
- 0.642 -> 0.5
- 2.534 -> 5.0
- median:
window_size: 7
send_every: 5
send_first_at: 3
- delta: 1%

# Sensor channel 1
- platform: ads1115
id: ch1
multiplexer: 'A1_GND'
gain: 4.096
update_interval: 1s
name: "Channel 1"
unit_of_measurement: "bar"
icon: "mdi:barometer"
device_class: "pressure"
state_class: "measurement"
accuracy_decimals: 2
filters:
- calibrate_linear:
- 0.642 -> 0.5
- 2.534 -> 5.0
- median:
window_size: 7
send_every: 5
send_first_at: 3
- delta: 1%
...


Add to Home-Assistant

HA-dev.png
ha.png

Now just add the ESPHome device to Home-Assistant and the values are available.

  1. Home Assistant -> Settings -> Devices & Services -> ESPHome -> “Add Device”
  2. Enter IP address, port can stay as is
  3. Enter “API encryption key”
  4. Done!

Now you can add the device to a dashboard. Here’s the Home-Assisant yaml code I’m using in the dashboard:

- type: grid
cards:
- type: heading
heading: Pumpe
heading_style: title
- type: entity
entity: switch.bewaesserung_pump
state_color: true
- graph: none
type: sensor
name: Druckdiffenrenz
entity: sensor.irrigation_pressure_pressure_difference
grid_options:
columns: 6
rows: 2
detail: 1
- type: gauge
entity: sensor.irrigation_pressure_channel_0
name: In
max: 4.5
needle: true
severity:
green: 2.8
yellow: 2.1
red: 0
grid_options:
columns: 6
rows: 2
- type: gauge
entity: sensor.irrigation_pressure_channel_1
name: Out
max: 4.5
needle: true
severity:
green: 2.8
yellow: 2.1
red: 0
grid_options:
columns: 6
rows: 2
- type: custom:mini-graph-card
name: Pumpendruck (last 6h)
entities:
- sensor.irrigation_pressure_channel_0
hours_to_show: 6
points_per_hour: 60
show:
labels: true
color_thresholds:
- value: 2.8
color: '#20aa20'
- value: 2.1
color: '#aaaa22'
- value: 0
color: '#FF1111'


Final Result & Summary

Of course, I had already spent many days thinking about the solution and ordering the necessary parts, but the actual build from breadboard to finished wall-mounted solution really only took one weekend.

The solution has been running for several weeks now and I’m very happy with it. Automations can be triggered and messages sent based on the sensor values. The history is also automatically logged in Home-Assistant. This way you can see exactly when the pump kicked in and how much pressure loss occurs overnight.

I had a lot of fun with this project, and I’ll solve the three problems I mentioned at the beginning next weekend with the appropriate automations and notifications :-)

Complete Code

esphome:
name: irrigation-pressure
friendly_name: Irrigation-Pressure

esp8266:
board: esp01_1m

# Enable logging
logger:
level: WARN

# Status LED
status_led:
pin:
number: GPIO02
inverted: true
# Enable Home Assistant API
api:
encryption:
key: "XXX" # <-------------- CHANGE ME

ota:
- platform: esphome
password: "XXX" # <-------------- CHANGE ME
- platform: web_server # Add this for web-based OTA uploads

web_server:
port: 80
version: 3

wifi:
ssid: !secret wifi_ssid_iot # <-------------- CHANGE ME
password: !secret wifi_password_iot # <-------------- CHANGE ME

# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Irrigation-Pressure"
password: "XXX" # <-------------- CHANGE ME

captive_portal:

i2c:
sda: GPIO04 # D2
scl: GPIO05 # D1
scan: true

ads1115:
- address: 0x48 # ADDR -> GND

font:
- file: "gfonts://Roboto"
id: small
size: 13

display:
- platform: ssd1306_i2c
model: "SSD1306 64X48"
address: 0x3C
lambda: |-
it.printf(2, 0, id(small), "0: %.2f bar", id(ch0).state);
it.printf(2, 16 , id(small), "1: %.2f bar", id(ch1).state);
it.triangle(2, 45, 7, 36, 12, 45);
it.printf(20, 33, id(small), "%.2f bar", id(difference).state);

button:
- platform: restart
icon: mdi:power-cycle
name: "ESP Reboot"

sensor:
# Sensor channel 0
- platform: ads1115
id: ch0
multiplexer: 'A0_GND'
gain: 4.096
update_interval: 1s
name: "Channel 0"
unit_of_measurement: "bar"
icon: "mdi:barometer"
device_class: "pressure"
state_class: "measurement"
accuracy_decimals: 2
filters:
- calibrate_linear:
- 0.642 -> 0.5
- 2.534 -> 5.0
- median:
window_size: 7
send_every: 5
send_first_at: 3
- delta: 1%

# Sensor channel 1
- platform: ads1115
id: ch1
multiplexer: 'A1_GND'
gain: 4.096
update_interval: 1s
name: "Channel 1"
unit_of_measurement: "bar"
icon: "mdi:barometer"
device_class: "pressure"
state_class: "measurement"
accuracy_decimals: 2
filters:
- calibrate_linear:
- 0.642 -> 0.5
- 2.534 -> 5.0
- median:
window_size: 7
send_every: 5
send_first_at: 3
- delta: 1%

# calculate difference between channel 0 and channel 1
- platform: template
name: "Pressure Difference"
id: difference
icon: "mdi:barometer"
unit_of_measurement: bar
accuracy_decimals: 2
lambda: 'return id(ch1).state - id(ch0).state;'

# Device RSSI
- platform: wifi_signal
name: "WiFi Signal"
entity_category: "diagnostic"
update_interval: 60s

text_sensor:
- platform: version
name: "ESPHome version"