15.04.2023, 12:34
Hi, I'm trying to setup underfloor heating with SE Valves
This is my setup/configuration, but I have some questions:
Group Addresses:
1/1/0 Test Room Setpoint Temp - (2 byte - 09-001 Temperature) - Temp setpoint visualization
1/1/1 Test Room Output Value - (1 byte - 05.001 scale) - SE Valve, Group Object: 0: Valve Actuating value - Drive to position
1/1/2 Test Room Actual Position - (1 byte - 05.001 scale) - SE Valve, Group Object: 2: Actual valve position - Indicate actual valve position
1/1/3 Test Room Heating On/Off - (1 bit - 01.001 switch) -
1/1/4 Test Room Window Contact - (1 bit - 01.009 open/close) - Binary input, Group Object: 0: Window open/close
1/1/5 Test Room Current Temp - (2 byte - 09-001 Temperature) - PIR with temp, Group Object: 11: Temperature sensor:Output
Scripting -> Common functions
Scripting -> Resident -> PID Test Room (Sleep interval 10 seconds)
This is my setup/configuration, but I have some questions:
- Is this how it should be done?
- If I want to use GA 1/1/3 to indicate if the heating is On or Off with 1 bit 01.001 switching how can I do that?
- What if I want heating in more rooms? Do I just make a new Residential script and according Group Addresses?
- he Manual mode, it says that the PID algorithm is stopped when this object value is 1, is it stopped or just paused? Should I use it to stop heating if my window is open in that room?
- Again about the Manual mode, I have window switches connected to a knx binary input, and the way I use it is with 1bit - 01.009, so that it says Close(1) if the window is closed and Open(0) if the window is open, but thats reversed, how to fix that?
Group Addresses:
1/1/0 Test Room Setpoint Temp - (2 byte - 09-001 Temperature) - Temp setpoint visualization
1/1/1 Test Room Output Value - (1 byte - 05.001 scale) - SE Valve, Group Object: 0: Valve Actuating value - Drive to position
1/1/2 Test Room Actual Position - (1 byte - 05.001 scale) - SE Valve, Group Object: 2: Actual valve position - Indicate actual valve position
1/1/3 Test Room Heating On/Off - (1 bit - 01.001 switch) -
1/1/4 Test Room Window Contact - (1 bit - 01.009 open/close) - Binary input, Group Object: 0: Window open/close
1/1/5 Test Room Current Temp - (2 byte - 09-001 Temperature) - PIR with temp, Group Object: 11: Temperature sensor:Output
Scripting -> Common functions
Code:
-- PID init, returns new PID object
function PID:init(params)
local n = setmetatable({}, { __index = PID })
local k, v
-- set user parameters
n.params = params
-- copy parameters that are set by user
for k, v in pairs(PID.defaults) do
if n.params[ k ] == nil then
n.params[ k ] = v
end
end
-- reverse gains in inverted mode
if n.params.inverted then
n.params.kp = -n.params.kp
n.params.ki = -n.params.ki
n.params.kd = -n.params.kd
end
return n
end
-- resets algorithm on init or a switch back from manual mode
function PID:reset()
-- previous value
self.previous = grp.getvalue(self.params.current)
-- reset iterm
self.iterm = 0
-- last running time
self.lasttime = os.time()
-- clamp iterm
self:clampiterm()
end
-- clamps iterm value
function PID:clampiterm()
self.iterm = math.max(self.iterm, self.params.min)
self.iterm = math.min(self.iterm, self.params.max)
end
-- clamp and set new output value
function PID:setoutput()
local t, object, value
self.output = math.max(self.output, self.params.min)
self.output = math.min(self.output, self.params.max)
value = math.floor(self.output)
local t = type(self.params.output)
-- write to output if object is set
if t == 'string' or t == 'table' then
if t == 'string' then
self.params.output = { self.params.output }
end
for _, output in ipairs(self.params.output) do
grp.write(output, value, dt.scale)
end
end
end
-- algorithm step, returns nil when disabled or no action is required, output value otherwise
function PID:run()
local result
-- get manual mode status
local manual = self.params.manual and grp.getvalue(self.params.manual) or false
-- in manual mode, do nothing
if manual then
self.running = false
-- not in manual, check if reset is required after switching on
elseif not self.running then
self:reset()
self.running = true
end
-- compute new value if not in manual mode
if self.running then
-- get time between previous and current call
local now = os.time()
self.deltatime = now - self.lasttime
self.lasttime = now
-- run if previous call was at least 1 second ago
if self.deltatime > 0 then
result = self:compute()
end
end
return result
end
-- computes new output value
function PID:compute()
local current, setpoint, deltasc, deltain, output
-- get input values
current = grp.getvalue(self.params.current)
setpoint = grp.getvalue(self.params.setpoint)
-- delta between setpoint and current
deltasc = setpoint - current
-- calculate new iterm
self.iterm = self.iterm + self.params.ki * self.deltatime * deltasc
self:clampiterm()
-- delta between current and previous value
deltain = current - self.previous
-- calculate output value
self.output = self.params.kp * deltasc + self.iterm
self.output = self.output - self.params.kd / self.deltatime * deltain
-- write to output
self:setoutput()
-- save previous value
self.previous = current
return self.output
end
Scripting -> Resident -> PID Test Room (Sleep interval 10 seconds)
Code:
-- init pid algorithm
if not p then
p = PID:init({
current = '1/1/5',
setpoint = '1/1/0',
output = '1/1/1',
manual = '1/1/4',
min = '0',
max = '100',
kp = '1',
ki = '1',
kd = '1'
})
end
-- run algorithm
p:run()