Lua / dzVents - Oil Tank Monitor

From Domoticz
Jump to navigation Jump to search

Purpose

The purpose of this script is to allow monitoring of a heating oil tank, by use of an ultrasonic sensor which measures the distance (of air) from the top of the fuel to the top of the tank.

The script is configured with the dimensions of the tank, and calculates the fuel level both as a percentage and as a volume in litres. It can also adjust to compensate for fuel expansion/contraction with temperature, to give a stable reading.

Dependencies

You will require a distance sensor which measures the air gap at the top of the tank. This is outside the scope of this example, but there are plenty of such devices on the market and many of them actually seem to be the same design, just rehoused and rebranded.

The Beckett Rocket, Watchman Sonic, and Apollo Ultrasonic oil tank monitors, and probably others, should all work with rtl_433 using a cheap USB DVB receiver. With a little more work, it should also be possible to use an RFM01 or RFM12B FSK receiver module on a Raspberry Pi, which should require less CPU.

Note that the RFXtrx433 modules are not suitable for this, as the modulation of the 433MHz signal is FSK. See here for the full details.

Others have made their own sensors.

Most sensors will also provide a temperature reading, since the speed of sound varies significantly across the temperature range that they need to operate within. However, is this is not required — the compensation for expansion of the fuel can be disabled if no temperature sensor is available.

Domoticz Setup

Input sensors

In my case, the input sensors are virtual; I haven't yet hooked up the rtl_433 receiver software to Domoticz directly, so I'm feeding values in externally. They were created as follows:

Temperature sensor (type 80):

curl 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=80'

Depth sensor (type 13):

curl 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=13'

Output sensors

Percentage sensor (type 2):

curl 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=2'

Volume sensor (RFXMeter type 113, subtype 2):

curl 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=113'
curl 'http://localhost:8080/json.htm?type=setused&idx=5&name=Oil&switchtype=2&used=true'

The idx=5 in the above will need to be replaced with the index of the created device in your own system.

(In fact, it's not clear that this is the correct device type to use. It's the only one I could find that has a unit of litres (or m³ in some cases), but it's designed for measuring water flow' not an amount. So the graphs aren't useful. We might need to add a new sensor type of plain volume to Domoticz.)

Installation instructions for the dzVents version

Having configured the devices as described above, all that remains is to edit the configuration in the script below, and place it into your scripts/dzVents/scripts directory.

dzVents Script with comments

--
-- dzVents script to calculate oil tank levels
-- Based on https://www.domoticz.com/wiki/Lua_-_Oil_Tank_Monitor
-- David Woodhouse <[email protected]>
--

-- Input devices: Temperature and air gap above fluid in tank
--local tank_temp_sensor = 'Oil tank temperature'
local tank_temp_sensor = 'Gas Temperature (left)'
local depth_sensor = 'Oil level sensor'

-- Output devices: Percentage full, volume.
local pct_sensor = 'Oil level'
local volume_sensor = 'Oil'

-- To adjust for fluid expansion
-- Report volume/percentage as they would be at 10°C
local canon_temp = 10
-- Kerosene
local expansion_coeff = 1.00099

-- Tank dimensions
local tank_height = 120
local tank_area = 60 * 181

return {
        on = {
                devices = {
                        depth_sensor
                }
        },
        execute = function(domoticz, device)
		local depthdev = domoticz.devices(depth_sensor)
		local tempdev = domoticz.devices(tank_temp_sensor)
		local pctdev = domoticz.devices(pct_sensor)
		local volumedev = domoticz.devices(volume_sensor)

		local depth = depthdev.state
		-- Calculate percentage and volume
		local pct = (tank_height - depth) / tank_height * 100
		local volume = (tank_height - depth) * tank_area / 1000

		-- Adjust for fluid expansion
		if tempdev ~= nil then
		   local temp = tempdev.state
		   local temp_delta = canon_temp - temp
		   local scale = expansion_coeff ^ temp_delta

		   -- temp_delta is how much colder the tank is than the canonical temperature
		   -- When the tank is colder, we scale *up* the reported volume to report it what it would be at 10°C
		   -- When the tank is warmer, we scale down.
		   -- domoticz.log('Scale factor ' .. scale .. ' for ' .. temp .. '°C delta ' .. temp_delta .. '°C', domoticz.LOG_INFO)
		   pct = pct * scale
		   volume = volume * scale
		end
		domoticz.log('Tank update to ' .. pct.. '% ' .. volume .. 'l', domoticz.LOG_INFO)
		pctdev.updatePercentage(pct)

		local prev_volume = volumedev.state + 0
		volume = -volume
		domoticz.log('Volume ' .. volume .. ' was ' .. prev_volume, domoticz.LOG_INFO)
		-- Don't allow fluctuations to screw up the usage calculations. Only go up if it's been refilled.
		if volume >= prev_volume or (volume < (prev_volume - 100)) then
		    -- domoticz.log('Raising volume from ' .. prev_volume .. ' to ' .. volume, domoticz.LOG_INFO)
		    volumedev.update(0, volume)
		else
		    -- domoticz.log('Not reducing volume from ' .. prev_volume .. ' to ' .. volume, domoticz.LOG_INFO)
		end
	end
}

Installation instructions for the Lua version

Having configured the devices as described above, all that remains is to edit the configuration in the script below, and place it into your scripts/lua directory.

Lua Script with comments

------------------------------------------------------------------------------
--
-- Copyright © 2015 David Woodhouse <[email protected]> and released under
-- the GNU General Public License, v2 or later.
--
--
-- Domoticz lua script to convert ultrasonic tank monitor readings into
-- percentage and volume virtual sensors for fuel tanks.
--
-- Takes input from distance sensor measuring the air above the fluid, and
-- converts to percentage and volume using the dimensions of the tank as
-- configured below.
--
-- Optionally, to prevent fluctuation as the fluid expands/contracts with
-- temperature, can convert the output values to report what the percentage
-- and volume *would* be at a fixed temperature.
--
------------------------------------------------------------------------------
--
-- Input sensors don't *have* to be virtual; mine are because they're filled in
-- by an external script running rtl_433 and receiving Watchman Sonic transmissions
--
-- Add tank temperature sensor:
-- 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=80'
-- Add depth sensor:
-- 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=13'
--
------------------------------------------------------------------------------
-- Output sensors are virtual, fed solely by this script
--
-- Add percentage sensor:
-- 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=2'
--
-- Add tank volume:
-- 'http://localhost:8080/json.htm?type=createvirtualsensor&idx=1&sensortype=113'
-- 'http://localhost:8080/json.htm?type=setused&idx=5&name=Oil&switchtype=2&used=true'
--
------------------------------------------------------------------------------
--
-- Update depth:
-- 'http://localhost:8080/json.htm?type=command&param=udevice&idx=3&nvalue=0&svalue=59'
--
------------------------------------------------------------------------------


-- Input devices: Temperature and air gap above fluid in tank
tank_temp_sensor = 'Oil tank temp'
depth_sensor = 'Oil tank sensor'

-- Output devices: Percentage full, volume.
pct_sensor = 'Oil tank'
pct_sensor_id = 4
volume_sensor = 'Oil'
volume_sensor_id = 5

-- To adjust for fluid expansion
-- Report volume/percentage as they would be at 10°C
canon_temp = -10
-- Kerosene
expansion_coeff = 1.00099

-- Tank dimensions
tank_height = 120
tank_area = 60 * 181

-----------------------------------------------------------------------------------
commandArray = {}

if (devicechanged[depth_sensor] or devicechanged[tank_temp_sensor]) then
   -- Use otherdevices_svalues[] because devicechanged[foo_Utility] is not
   -- present when the value is zero
   depth = otherdevices_svalues[depth_sensor]

   -- Calculate percentage and volume
   pct = (tank_height - depth) / tank_height * 100
   volume = (tank_height - depth) * tank_area / 1000

   -- Adjust for fluid expansion
   tank_temp = otherdevices_svalues[tank_temp_sensor]
   if (tank_temp ~= nil) then
      temp_delta = tank_temp - canon_temp
      scale = expansion_coeff ^ temp_delta
      pct = pct * scale
      volume = volume * scale
   end

-- print(string.format("depth now %f; percentage %f %% volume %f l", depth, pct, volume))

   commandArray[1] = {['UpdateDevice'] = pct_sensor_id .. "|0|" .. pct}
   commandArray[2] = {['UpdateDevice'] = volume_sensor_id .. "|0|" .. volume}
end

return commandArray

Example of use (if relevant) i.e. output files / screen displays

Domoticz displaying oil tank levels