Ultrasonic Home Heating Oil Gauge

Ultrasonic Home Heating Oil Gauge
Photo by Zbynek Burival / Unsplash

An ultrasonic sensor sends a sound pulse that bounces off the oil and returns to the sensor.  The amount of time that it takes can be measured and based on the speed of sound you can figure out the distance.  Neat.  It's nothing as grand as the pic above, I'm just measuring the levels in my 275 gallon tank at home. I once caught the oil guy filling my tank and he was impressed when I guessed within 2 gallons of his fill reading.

I'm using micropython with an ESP32, MQTT, and Zabbix to keep historical data though I recently switched to InfluxDB.



Supplies

When you purchase through links on this site, I may earn an affiliate commission.


Wiring


Code

Getting Started With Micropython

1. Getting started with MicroPython on the ESP32 — MicroPython latest documentation

micropython-mqtt

GitHub - peterhinch/micropython-mqtt: A ‘resilient’ asynchronous MQTT driver. Plus a means of using an ESP8266 to bring MQTT to non-networked targets.
A 'resilient' asynchronous MQTT driver. Plus a means of using an ESP8266 to bring MQTT to non-networked targets. - GitHub - peterhinch/micropython-mqtt: A 'resilient' asynchronous M…

Grab mqtt_as/mqtt_as.py and mqtt_as/mqtt_local.py and upload them to the root of the ESP32 without the subdirs.  We'll need to tweak mqtt_local.py with your WiFi and MQTT broker settings.

config['server'] = '<broker hostname>'

config['ssid'] = '<SSID>'
config['wifi_pw'] = '<WiFi password>'
config['ssl'] = <True | False>
config['port'] = <broker port>
config['user'] = '<broker user>'
config['password'] = '<broker password>'
Sample mqtt_local.py updates.

micropython-hcsr04

GitHub - rsc1975/micropython-hcsr04: Micropython driver for ultrasonic sensor HC-SR04
Micropython driver for ultrasonic sensor HC-SR04. Contribute to rsc1975/micropython-hcsr04 development by creating an account on GitHub.

Grab hcsr04.py and upload it.

micropython-ssd1306 (optional if not using display)

GitHub - stlehmann/micropython-ssd1306: A fork of the driver for SSD1306 displays to make it installable via upip
A fork of the driver for SSD1306 displays to make it installable via upip - GitHub - stlehmann/micropython-ssd1306: A fork of the driver for SSD1306 displays to make it installable via upip

Grab ssd1306.py and upload it.

micropython-nano-gui (optional if not using display)

GitHub - peterhinch/micropython-nano-gui: A lightweight MicroPython GUI library for display drivers based on framebuf class
A lightweight MicroPython GUI library for display drivers based on framebuf class - GitHub - peterhinch/micropython-nano-gui: A lightweight MicroPython GUI library for display drivers based on fram…

Grab drivers/boolpalette.py and gui/core/writer.py and upload them to the root of your ESP32 without the subdirs.

This project's code

GitHub - scarey/heating-oil-gauge: Micropython code for reading home heating oil levels with an ultrasonic sensor.
Micropython code for reading home heating oil levels with an ultrasonic sensor. - GitHub - scarey/heating-oil-gauge: Micropython code for reading home heating oil levels with an ultrasonic sensor.

Historical Data

I have a old Zabbix server which has a couple years of oil data but I'm moving to InfluxDB and Telegraf for new projects.  In the images above you can see a sample of each.

A dashboard with the outside temperature, thermostat call history, and oil tank levels can be very interesting to look at.  Or maybe after your teenager just took a 45 minute shower you can take a peek at how much that just cost 🤦.

[[outputs.influxdb_v2]]
  urls = ["http://<host>:8086"]
  token = "<your token>"
  organization = "<your org>"
  bucket = "iot"

[[inputs.mqtt_consumer]]
  servers = ["mqtts://<host>:<port>"]
  topics = [
    "esp32/oil/gallons",
  ]
  qos = 0
  username = "telegraf"
  password = "<mqtt password>"
  insecure_skip_verify = false
  data_format = "value"
  data_type = "float"
  topic_tag = ""

[[inputs.mqtt_consumer.topic_parsing]]
  topic = "esp32/oil/gallons"
  tags = "source/_/field"
  measurement = "_/measurement/_"
  fields = "_/_/gallons"
  [[processors.pivot]]
    tag_key = "field"
    value_key = "value"
Sample telegraf configuration

Notifications

See my Laundry's Done post for some notification options.

Laundry’s Done!
Well there’s still folding and stuff left but getting notified when the washer/dryer cycle finished was one of my first projects and is a big hit with the wife. An accelerometer detects when motion has ceased and sends a notification. I’m using micropython with an ESP32, MQTT, Home Assistant,

An obvious notification would be when your tank meets some low level threshold and it's time to order a fill.  A fun one could be a fill notification maybe using InfluxDB to detect a large increase over a short time.  Or a more frightening large decrease over a short time!


Assembly

PVC

The ultrasonic sensor is mounted face down on a rubber disc with the transmitter and receiver pushed through holes in the disc.  The rubber disc is inserted into the lower adapter and the cleanout adapter is cemented on top to form the enclosure. The ESP32 will be inside the cleanout and the whole thing will be screwed into an available spot in the top of the tank. The top plug will allow easy access to the ESP32 and the rubber disc isolates the oil from the majority of the electronics. Mine's been running non-stop for more than 3 years now.

  1. Use the inside of the wide end of the lower adapter as a template (probably about 2.375") and cut a circle out of the rubber sheet.  The goal is to have a rubber disc that can fit nicely inside the lower adapter.
  2. Using the ultrasonic sensor as a template, center it on the newly cut rubber disc and mark a circle for the transmitter and one for the receiver.  Cut those circles and insert the sensor into the holes.  To give yourself more room you could change out the sensor pins so they point straight out the back of the sensor.
  3. Connect your dupont wires to the sensor and insert the disc into the lower adapter as far down as it will go.  Maybe use some thread sealant around the edge of the rubber disc if it isn't making a good seal.
  4. Cement the cleanout adapter to the upper side of the lower adapter.  I added some duct tape around the 2 pieces for some added elegance.
  5. Drill a hole below the threads of the cleanout adapter so you can run the power wiring.  I made a hole big enough to fit a micro USB cable through.
  6. Remove a plug from the top of the oil tank, put some pipe thread sealant around the threads of the lower adapter of the enclosure and screw it on the tank.
  7. Estimate how far the ultrasonic sensor is from the top of the inside of the tank and save that number for SENSOR_OFFSET in main.py.

Display mount (optional if not using display)

  1. Figure out which side you want the power wiring to exit and drill another hole where you'd like the display.
  2. You can 3D print the display mount that I modified if you like or come up with some other mount.  You can grab it below.

Notes

  • My tank is an upright 275 gallon tank that's pretty common for residential use in the US.  The inches_to_gallons.py contains a dictionary of oil height in 0.1 inch increments to the number of gallons of oil that represents.  If you're tank is different you'll have to create your own dictionary and also tweak MAX_INCHES in main.py which is used to detect bad readings.  And of course rename stuff if you're using cm and liters, etc.  You also don't need to go that crazy with the precision as long as you round the measurement to a value in the inches_to_gallons dictionary.
  • I've included some code to detect readings that seem off and do a retry.  That seems to work well for my setup but you could change that to averaging multiple readings or whatever you think would work better.
Technology icons created by Freepik - Flaticon