Oil Tank Level Monitor Watchman Sonic rtl_433 Integration for RF sensors or level sensors

Hi, did you manage to get any further with your setup using the ESPHome component? Which RF receiver are you using?

This is an awesome project, so thank you OP for the detailed write up. This is definitely on my project list. Like @alphabeta279, I too thought that it would be great to not have to dedicate a RPi for this, and have this working with ESP-Home somehow. I don’t know anything about RF, as far as I understand, the RF-Switch would not work with this as it isn’t concerned with decoding the various device protocols.

There is hope however as there is work on bringing the RTL433 to arduino for use with ESP Home. https://github.com/NorthernMan54/rtl_433_ESP
The project doesn’t yet fully support the type of modulation the Watchman Sonic uses, but it looks like it is getting close. Note that the transceiver being used for this project is the CC1101.

I just thought I’d bring this to attention of those who are interested in this brilliant project, but wonder if this could be done with an ESP32.

2 Likes

Great project! Would it be possible to use this with the Broadlink RM4?

Just to make it clear, I have nothing to do with these amazing project, I’ve merely been googling the topic. So I’m afraid I can’t answer that question.

Thanks dingledell for trailblazing this implementation. I wanted to leverage it to shoehorn my estimated oil usage into the new energy management system in Home Assistant (as mentioned here Energy Management - Allow 'Oil' in addition to 'Gas').

The problem I came across is that the variance on the sonar measurement is quite high, can vary by 2-3cm in depth throughout the day due to ambient temperature etc… To overcome this I tied together a bunch of things to ensure I only ever take the minimum measurement, with “fill up” detection, and compute the difference in volume from measurement to measurement manually.

I followed your suggestion on setting up MQTT to send the rtl-433 data across to home assistant:

- platform: mqtt
  state_topic: "home/rtl_433"
  name: "Raw oil measurement"
  value_template: >-
    {% set x = 0.52 %}
    {% set y = (104 - (value_json.depth_cm | int(0)) + 5) | float(0) * 0.01 %}
    {% set z = 1.63 %}
    {{ ((x*x*acos((x-y)/x) - (x-y)*sqrt(2*x*y-y*y))*z*1000) | round(0,default=0) }}
  unit_of_measurement: 'L'
  state_class: measurement
  availability_template: >- 
    {{ (value_json.depth_cm | int(0)) > 0 }}

I have a horizontal cylinder so I compute the volume accordingly. Then I have a couple of input numbers I use to store persistent variables and a template variable tracking the change in oil volume between measurements:

input_number:
  curr_oil:
    name: Current Oil
    unit_of_measurement: L
    min: 0
    max: 1380
    step: 1
  cumulative_oil:
    name: Cumulative Oil
    unit_of_measurement: L
    min: 0
    max: 16777215
    step: 1

- platform: template
  sensors:
    oil_measurement_diff:
       friendly_name: "Oil Measurement Diff"
       unit_of_measurement: "L"
       value_template: '{{ states("input_number.curr_oil") | int(0) - states("sensor.raw_oil_measurement") | int(0) }}'
       availability_template: >- 
        {{ not is_state("sensor.raw_oil_measurement", "unavailable") and not is_state("sensor.raw_oil_measurement", "unknown") }}

Then my automation, whenever a new MQTT measurement arrives, if the volume of the tank has increased by 100L, I assume it was filled up and just set the current level to that amount. Otherwise, if the volume of the tank is lower than the lowest value read since the last time it was filled up, I add the difference in volume to the cumulative volume change and update the current (lowest) level:

  alias: Oil Aggregation
  description: ''
  trigger:
  - platform: event
    event_type: state_changed
    event_data:
      entity_id: sensor.oil_measurement_diff
  condition: []
  action:
  - choose:
    - conditions:
      - condition: numeric_state
        entity_id: sensor.oil_measurement_diff
        below: '-100'
      sequence:
      - service: input_number.set_value
        data_template:
          value: '{{ states(''sensor.raw_oil_measurement'') | int(0) }}'
        target:
          entity_id: input_number.curr_oil
    - conditions:
      - condition: numeric_state
        entity_id: sensor.oil_measurement_diff
        above: '0'
      sequence:
      - service: input_number.set_value
        data_template:
          value: '{{ states(''input_number.cumulative_oil'') | int(0) + states(''sensor.oil_measurement_diff'')
            | int(0) }}'
        target:
          entity_id: input_number.cumulative_oil
      - service: input_number.set_value
        data_template:
          value: '{{ states(''sensor.raw_oil_measurement'') | int(0) }}'
        target:
          entity_id: input_number.curr_oil
    default: []
  mode: single

Finally I have another template to put the cumulative volume used into a format consumable by the gas consumption energy integration:

template:
  - sensor:
      - name: "Oil Energy"
        state: >-
             {{ states('input_number.cumulative_oil') | float(0) * 10.35  }}
        device_class: energy
        state_class: total
        unit_of_measurement: "kWh"

My sensor updates hourly but my usage is so low I don’t expect to be able to track things at a high temporal resolution, more likely I’ll see a sudden drop to a lower depth value some days. Over week timescales it should be nice and smooth.

2 Likes

Very cool, i have a Watchman Sonic. I have spare Raspberry Pi’s, i have a spare very small 433mhz receiver. Do you use your RPi instead of the original watchman unit or do you have both running?

Not sure if it the 433mhz receiver i have will be good enough although the original watchman isnt massive.

I have them both running, they don’t interfere.

I’ve already built an ultrasonic oik tank device, but also have an existing old watchman plus device which was already on the tank when we moved in. This still works and transmits to a plug in receiver and shows a number from 1-9 to indicate oil level.

If I can intercept the RF broadcast transmitting between the sender and receiver, I can repurpose my home made ultrasonic device elsewhere.

I have a Sonoff RF Bridge v2.2 which I have modified to bypass thr RF chip, and also a CC1101 433mhz device which I have connected to an ESP32 board.

There doesn’t seem to be a suitable combination available to achieve this, via either Tasmota or ESPHome. The RTL_433 ports available don’t support the FSK modulation that the watchman uses. The windows port of RTL_433 doesn’t support the cc1101 plugged into an esp board, only a dedicated sdr.

Can anyone suggest a combination of software and hardware that would achieve this?

Hi there, I am new to home assistant, and trying to get this working as you have. I have the watchman showing temperature and dept, but can/t seem to get it to display in litres or any other statistics.

I was wondering can I create a sensor that converts it?
I was thinking something like this but I don’t know what I am doing, can anybody show me where I am going wrong please?

    template:
     - sensor:
       - name: "Oil Tank Air at Top"
         state:  "sensor.oil_tank_d"
         value_template: "{{ value_json.depth_cm }}"
         unit_of_measurement: 'cm'

Hi There.
Could you tell me where you found the equations for the horizontal cylinder you are using. I’ve a similar one, but probably with different dimensions, but I’m not sure in your figures what relates to height/length/diameter etc? Mine is a TItan ES1300B (Bunded) tank.

Hi,

My tank is 120cm diameter and in turn this 60cm radius. Just take the radius and muliply it by itself (this is the radius squared), 60cmx60cm= 3600 x 3.142 (Pi) = 11311.2 cm² in area for for the base, for 1cm high this is 11311.2cm³ (this is cubic centimetres, then divide this by 1000 to get the volume per cm height in litres, as 1 litre in volume is 10cm x 10cm area x 10cm high = 1000cm³ in one cubic litre)

If you want total volume by the height of tank use the same as above but replace the 1cm high for the total height- radius cm x radius cm x 3.142 x height cm = xxx cm³ divided by 1000 for litres

Or use this :wink: with your dimensions input it should work out the same roughly as the above calculations https://www.omnicalculator.com/math/cylinder-volume

1 Like

Hi, it should work if you follow the process and have a sensor like this but you would need your own sensor ID (if there are no others in the area you dont need to have this ID with the rtl_demultiplexer as I litsed above on Jan 1, I have loads in the area). You will see all the devices when you are receieving signals in Putty or simlar.

#this pulls json data from an automation that gives the data from the oil sonic a ref relevant to the ID as more than one in the area, see above

  - platform: mqtt
    name: "Air Gap"
    state_topic: "home/sensor137449098"
    value_template: "{{value_json.depth_cm}}"
    unit_of_measurement: 'cm'
    force_update: true

Hi, nice additions, I tried a rapid change in state sensor to detect oil fill or rapid drop with alert email / pushbullet but its never really worked and haven’t had time to test, investigate or develop. It was based on adding a binary sensor for trends.

#oil alerts
  - platform: trend
    sensors:
      rapid_oil_drop:
        max_samples: 3
        entity_id: sensor.air_gap
        #watchman updated 08:32 43s, 08:49 40s, 09:06: 37s, 09:23 34s, updates evey 17min 57s or every 1077 seconds, 3 samples is 3231s so set to 3300s for sample duration to collect samples
        #the gradient is sensor units per second change that is permissable before changing state so 2cm in 2 minutes would be 2cm divide 60 secs = 0.03333 so call 0.034 change per second over 1 minute
        sample_duration: 3300
        min_gradient: 0.017
        #based on direct air gap mqtt sensor so plus figure for air gap gradient as this is a drop in oil the larger the gap   
  - platform: trend
    sensors:
      rapid_oil_rise:
        max_samples: 3
        entity_id: sensor.air_gap
        #watchman updated 08:32 43s, 08:49 40s, 09:06: 37s, 09:23 34s, updates evey 17min 57s or every 1077 seconds, 3 samples is 3231s so set to 3300s for sample duration to collect samples
        #the gradient is sensor units per second change that is permissable before changing state so 2cm in 2 minutes would be 2cm divide 60 secs = 0.03333 so call 0.034 change per second over 1 minute
        sample_duration: 3300
        min_gradient: -0.017
        #based on direct air gap mqtt sensor so minus figure for air gap gradient as this is an increase in oil the smaller the gap

Hi, you could use automations based on sensor values to transmit IR codes from the Broadlink to IR devices using the broadlink transmit commands, I didnt really see the merit in this for me.

Thanks to all for your work here. My watchman plus indicator unit has just gone bang (kitchen steam ingress :frowning_face:) so rather than buying a new unit, I’ll do the above. I already run MQTT and have a nearish original Rpi for monitoring - I don’t think the CPU load will be too great.

After a false start due to too weak signal from the level sensor, I have changed the AAA batteries in the battery tube and moved the receiver and its antenna closer to the oil tank. As well as now receiving my oil data, I still get the neighbours weather station and now the tyre pressures of passing vehicles on the road outside. But now it is just a matter of permanently fixing a weatherproof antenna cut for 433.920 at front and setting up HA as described above. I’m so pleased with the precious work you have done.

{"time" : "2022-09-27 16:51:48", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 147, "wind_avg_m_s" : 0.140, "wind_max_m_s" : 1.120, "rain_mm" : 1052.700, "uv" : 58, "uvi" : 0, "light_lux" : 885.600, "mic" : "CRC"}
{"time" : "2022-09-27 16:51:57", "model" : "Nexus-TH", "id" : 44, "channel" : 1, "battery_ok" : 1, "temperature_C" : 12.600, "humidity" : 54}
{"time" : "2022-09-27 16:52:12", "model" : "Abarth-124Spider", "type" : "TPMS", "id" : "0d6519d5", "flags" : "67", "pressure_kPa" : 220.800, "temperature_C" : 23.000, "status" : 94, "mic" : "CHECKSUM"}
{"time" : "2022-09-27 16:52:20", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 137, "wind_avg_m_s" : 0.420, "wind_max_m_s" : 2.240, "rain_mm" : 1052.700, "uv" : 59, "uvi" : 0, "light_lux" : 885.600, "mic" : "CRC"}
{"time" : "2022-09-27 16:52:54", "model" : "Nexus-TH", "id" : 44, "channel" : 1, "battery_ok" : 1, "temperature_C" : 12.600, "humidity" : 54}
{"time" : "2022-09-27 16:53:24", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 76, "wind_avg_m_s" : 0.140, "wind_max_m_s" : 1.120, "rain_mm" : 1052.700, "uv" : 58, "uvi" : 0, "light_lux" : 923.400, "mic" : "CRC"}
{"time" : "2022-09-27 16:53:40", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 180, "wind_avg_m_s" : 0.280, "wind_max_m_s" : 1.120, "rain_mm" : 1052.700, "uv" : 58, "uvi" : 0, "light_lux" : 939.600, "mic" : "CRC"}
{"time" : "2022-09-27 16:53:51", "model" : "Nexus-TH", "id" : 44, "channel" : 1, "battery_ok" : 1, "temperature_C" : 12.600, "humidity" : 54}
{"time" : "2022-09-27 16:53:56", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 141, "wind_avg_m_s" : 0.840, "wind_max_m_s" : 1.120, "rain_mm" : 1052.700, "uv" : 58, "uvi" : 0, "light_lux" : 955.800, "mic" : "CRC"}
{"time" : "2022-09-27 16:54:17", "model" : "Abarth-124Spider", "type" : "TPMS", "id" : "0c8b041a", "flags" : "67", "pressure_kPa" : 278.760, "temperature_C" : 18.000, "status" : 97, "mic" : "CHECKSUM"}
{"time" : "2022-09-27 16:54:17", "model" : "Abarth-124Spider", "type" : "TPMS", "id" : "0c8b041a", "flags" : "67", "pressure_kPa" : 278.760, "temperature_C" : 18.000, "status" : 97, "mic" : "CHECKSUM"}
{"time" : "2022-09-27 16:54:17", "model" : "Abarth-124Spider", "type" : "TPMS", "id" : "0c8b041a", "flags" : "67", "pressure_kPa" : 278.760, "temperature_C" : 18.000, "status" : 97, "mic" : "CHECKSUM"}
{"time" : "2022-09-27 16:54:25", "model" : "Hyundai-VDO", "type" : "TPMS", "id" : "5cb631f6", "state" : 224, "flags" : 2, "repeat" : 9, "pressure_kPa" : 193.875, "temperature_C" : 12.000, "maybe_battery" : 165, "mic" : "CRC"}
{"time" : "2022-09-27 16:54:25", "model" : "Hyundai-VDO", "type" : "TPMS", "id" : "5cb631f6", "state" : 224, "flags" : 2, "repeat" : 10, "pressure_kPa" : 193.875, "temperature_C" : 12.000, "maybe_battery" : 165, "mic" : "CRC"}
{"time" : "2022-09-27 16:54:44", "model" : "Fineoffset-WH24", "id" : 204, "battery_ok" : 1, "temperature_C" : 11.300, "humidity" : 73, "wind_dir_deg" : 136, "wind_avg_m_s" : 0.140, "wind_max_m_s" : 1.120, "rain_mm" : 1052.700, "uv" : 58, "uvi" : 0, "light_lux" : 988.200, "mic" : "CRC"}
{"time" : "2022-09-27 16:54:48", "model" : "Nexus-TH", "id" : 44, "channel" : 1, "battery_ok" : 1, "temperature_C" : 12.500, "humidity" : 54}

I show the responses for amusement but of course I shall filter the devices in HA.
Thanks again Tony

Hi, would also appreciate an update when you have a minute? Like the idea of using ESPHome rather than a Pi as a low-cost basic sensor. Did you ever get it working? Same as @Scoff would be great to know what hardware components you have validated if any. Many thanks.

Same here. Been following this thread as it’s on my hit list of new additions for my HA setup.

Can any 433 receiver work with this or do you need one of those Realtek dongles?

In an attempt to improve the data from the watchman, I have added a filter between the raw reading and the calculations. I noticed that a mini graph with an effective moving average over a period of 24 hours actually looks quite sensible, despite individual readings varying as much as 3cm within 15 minutes. The config looks like this:

mqtt:
    sensor:
      - name: "Oil Tank Raw"
        state_topic: "home/rtl_433"
        value_template: "{{ value_json.depth_cm }}"
        unit_of_measurement: 'cm'

sensor:
  - platform: filter
    name: "Oil Tank Air at Top"
    entity_id: sensor.oil_tank_raw
    filters:
      - filter: lowpass
        time_constant: 10
      - filter: time_simple_moving_average
        window_size: "12:00"
        precision: 2

template:
  - sensor:
      - name: "Oil Litres"
        unit_of_measurement: 'Litres'
        # x = tank radius, y = depth of oil, z = tank length
        state: >
          {% set x = 0.58 %}
          {% set y = ((x*2*100) - (states('sensor.oil_tank_air_at_top') | float) + 2) * 0.01 %}
          {% set z = 1.55 %}
          {{ ((x*x*acos((x-y)/x) - (x-y)*sqrt(2*x*y-y*y))*z*1000 - 433) | round(0,default=0) }}
      - name: "Oil Percent"
        unit_of_measurement: '%'
        state: >
          {{ (100 * (states('sensor.oil_litres') | float) / 1200) | round(0,default=0) }}
      - name: "Oil Depth Left"
        unit_of_measurement: 'cm'
        state: >
          {{ 82 - (states('sensor.oil_tank_air_at_top') | float) | round(2,default=0) }}