This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Solar Heating Controller
#1
Dears,

as i was not happy with the controlability of my Solar Heating System (mainly only read out not able to control)

I found a way to change 0-100% to 0-10V to PWM signal by using the Phoenix 2902032 module. As i am not a pro in coding and making it in FBeditor would be a painstaking process is asked AI to help me and came up with this.

Function wise i think it will work and i will try and keep you posted. If somebody could check the coding i would really appriciate.

Posting all here to have others be able to make use of it too!

This script is a comprehensive control solution for a dual-boiler solar thermal system, designed for the LogicMachine environment. It balances heat optimization, equipment longevity, and multi-stage safety.
Here is the breakdown of how the logic operates during a typical daily cycle.


1. The Decision Engine (Priority & Selection)
The script first determines which boiler needs heat. It follows a Priority Logic:
  • Boiler 1 (DHW): Usually set as Priority 1. The system checks if it can heat this tank first.
  • Boiler 2 (Buffer): If Boiler 1 is "Full" (reached its Standard Temp) or if the sun isn't hot enough for Boiler 1 but is hot enough for Boiler 2, it switches the 3-way valve.
The function check_boiler handles the math. It calculates the difference ($\Delta T$) between the roof and the tank.
  • To Start: $T_{collector} > T_{boiler} + \Delta T_{start}$.
  • To Stay On: $T_{collector} > T_{boiler} + \Delta T_{stop}$.
    This "gap" between start and stop (Hysteresis) prevents the pump from clicking on and off rapidly.


2. Intelligent Pump Control (PID & Kickstart)
Once a boiler is selected, the script calculates how fast the pump should spin:
  • The Kickstart: Solar pumps often struggle with air bubbles or fluid inertia. For the first few seconds (ga_kickstart_sec), the pump is forced to 100%.
  • Proportional Speed (P-Logic): After kickstart, the speed scales. If the roof is only slightly warmer than the tank, the pump runs at your Minimum Speed (e.g., 25%) to move heat slowly. As the roof gets much hotter (approaching the PID Range), the pump speeds up to 100% to maximize heat transfer.
  • Anti-Cycling (Min Run Time): If the sun disappears behind a cloud, the min_run_sec timer keeps the pump running for a few minutes. This ensures the system doesn't stop just as the fluid in the pipes reaches the heat exchanger.


3. Multi-Stage Safety & Protection
Solar systems can reach dangerous temperatures in summer. This code includes three layers of defense:
  1. Standard Limit: Stops heating a tank at a setpoint (e.g., 60°C) to prevent scalding and save energy.
  2. Overheat Heat Dump: If the Collector hits a high limit (e.g., 110°C), the script ignores the Standard Limit and pushes heat into the boilers up to their Absolute Max (e.g., 90°C) to try and cool the roof down.
  3. Stagnation Stop: If the collector reaches the Danger Temp (e.g., 140°C), the fluid has likely turned to steam. Pumping steam destroys pumps. The script executes an Emergency Stop.
  4. Reverse Flow Protection: If the pipe returning from the roof is hotter than the roof itself, it means you are accidentally heating the outdoors. The script detects this and stops the pump.


4. Maintenance & Manual Override
The Maintenance Mode (Object 30/1/20) allows you to take manual control:
  • Auto (0): The logic described above runs the system.
  • Manual Off (1): Forces the pump and relay OFF. Use this for repairs.
  • Manual On (2): Forces the pump to 100% and opens the relay. Use this to bleed air or test the pump.


5. Hardware Interfacing (Outputs)
The script controls two different types of hardware:
  • The Relay (ga_pump_run): A binary ON/OFF command. This is used to physically power up the pump.
  • The Speed (ga_pump_speed): A 0–100% signal. This is usually converted by a LogicMachine AO (Analog Output) module to a 0–10V signal for the pump's PWM input.
  • The Valve (ga_valve_pos): A binary signal to the 3-way valve to switch between Tank 1 and Tank 2.

Code:
-- [[ SOLAR CONTROL SYSTEM: PID, KICKSTART, OVERHEAT, MIN-RUN & MAINTENANCE ]] --

-- --- 1. SENSOR INPUTS ---
local ga_coll_temp = '30/1/1'      -- Roof Collector Temperature
local ga_ret_temp  = '30/1/2'      -- Return Pipe Temperature (before roof)
local ga_b1_temp   = '30/1/3'      -- Boiler 1 Temperature (Priority)
local ga_b2_temp   = '30/1/4'      -- Boiler 2 Temperature (Secondary)

-- --- 2. OPERATIONAL SETTINGS ---
local ga_priority = '30/1/5'       -- Switch: 1 = Boiler 1, 2 = Boiler 2
local ga_b1_std_temp = '30/1/6'    -- Target Temp Boiler 1 (e.g. 60°C)
local ga_b1_max_temp = '30/1/7'    -- Safety Limit Boiler 1 (e.g. 90°C)
local ga_b2_std_temp = '30/1/8'    -- Target Temp Boiler 2
local ga_b2_max_temp = '30/1/9'    -- Safety Limit Boiler 2

-- --- 3. DIFFERENTIAL (DELTA T) SETTINGS ---
local ga_b1_delta_start = '30/1/10' -- Delta T needed to START heating B1
local ga_b1_delta_stop  = '30/1/11' -- Delta T where B1 heating STOPS
local ga_b2_delta_start = '30/1/12' -- Delta T needed to START heating B2
local ga_b2_delta_stop  = '30/1/13' -- Delta T where B2 heating STOPS

-- --- 4. PUMP PID & BEHAVIOR SETTINGS ---
local ga_pid_range      = '30/1/14' -- Delta T range for scaling pump to 100%
local ga_min_pump_speed = '30/1/15' -- Absolute minimum speed (e.g. 25%)
local ga_kickstart_sec  = '30/1/16' -- 100% power duration on startup (sec)
local ga_min_run_min    = '30/1/17' -- Minimum pump run time (minutes)

-- --- 5. PROTECTION & MAINTENANCE ---
local ga_coll_overheat  = '30/1/18' -- Temp to start emergency heat dump
local ga_coll_danger    = '30/1/19' -- Stagnation temp to STOP pump
local ga_maint_mode     = '30/1/20' -- NEW: 0=Auto, 1=Manual OFF, 2=Manual ON (100%)

-- --- 6. OUTPUTS ---
local ga_pump_speed = '30/1/21'     -- Pump Speed Output (0-100%)
local ga_pump_run   = '30/1/22'     -- Pump Power Relay (On/Off)
local ga_valve_pos  = '30/1/23'     -- Valve Position (0=B1, 1=B2)

-- --- DATA FETCHING ---
local t_coll = grp.getvalue(ga_coll_temp)
local t_ret  = grp.getvalue(ga_ret_temp)
local t_b1, t_b2 = grp.getvalue(ga_b1_temp), grp.getvalue(ga_b2_temp)
local maint_mode = grp.getvalue(ga_maint_mode) or 0 -- Default to Auto

local p1_start, p1_stop = grp.getvalue(ga_b1_delta_start) or 8, grp.getvalue(ga_b1_delta_stop) or 3
local p2_start, p2_stop = grp.getvalue(ga_b2_delta_start) or 8, grp.getvalue(ga_b2_delta_stop) or 3
local b1_std, b1_max = grp.getvalue(ga_b1_std_temp) or 60, grp.getvalue(ga_b1_max_temp) or 90
local b2_std, b2_max = grp.getvalue(ga_b2_std_temp) or 60, grp.getvalue(ga_b2_max_temp) or 90

local min_speed = grp.getvalue(ga_min_pump_speed) or 20
local min_run_sec = (grp.getvalue(ga_min_run_min) or 3) * 60
local now = os.time()

-- Current Status
local last_speed = grp.getvalue(ga_pump_speed)
local current_valve = grp.getvalue(ga_valve_pos)
local pump_was_off = (last_speed == 0)

-- --- SAFETY CHECKS ---
local is_overheating = (t_coll >= (grp.getvalue(ga_coll_overheat) or 110))
local is_danger = (t_coll >= (grp.getvalue(ga_coll_danger) or 140))
local is_reverse = (not pump_was_off) and (t_ret > t_coll + 2)

-- --- LOGIC FUNCTION ---
function check_boiler(id, t_curr, t_std, t_max, d_start, d_stop)
    local diff = t_coll - t_curr
    local currently_active = (not pump_was_off and ((id == 1 and not current_valve) or (id == 2 and current_valve)))
    local limit = is_overheating and t_max or t_std
   
    if t_curr >= t_max then return false, 0 end
   
    if currently_active then
        local start_t = storage.get('solar_start')
        local forced_run = start_t and (now - start_t < min_run_sec)
        if forced_run then return true, diff end
        if diff > d_stop and t_curr < limit and not is_reverse then return true, diff end
    else
        if diff > d_start and t_curr < limit then return true, diff end
    end
    return false, 0
end

-- --- MAIN LOGIC ---
local target_boiler = 0
local active_diff = 0
local priority = grp.getvalue(ga_priority) or 1
local speed = 0
local run_relay = false

-- MAINTENANCE OVERRIDE
if maint_mode == 1 then -- MANUAL OFF
    target_boiler = 0
    speed = 0
    run_relay = false
elseif maint_mode == 2 then -- MANUAL ON
    target_boiler = 1 -- Default to B1 for manual test
    speed = 100
    run_relay = true
else -- AUTOMATIC MODE
    local b1_ok, b1_diff = check_boiler(1, t_b1, b1_std, b1_max, p1_start, p1_stop)
    local b2_ok, b2_diff = check_boiler(2, t_b2, b2_std, b2_max, p2_start, p2_stop)

    if is_danger then
        target_boiler = 0
    elseif priority == 1 then
        if b1_ok then target_boiler, active_diff = 1, b1_diff
        elseif b2_ok then target_boiler, active_diff = 2, b2_diff end
    else
        if b2_ok then target_boiler, active_diff = 2, b2_diff
        elseif b1_ok then target_boiler, active_diff = 1, b1_diff end
    end

    if target_boiler > 0 then
        run_relay = true
        if is_overheating then
            speed = 100
        else
            local stop_d = (target_boiler == 1) and p1_stop or p2_stop
            speed = ((active_diff - stop_d) / (grp.getvalue(ga_pid_range) or 12)) * 100
            if speed < min_speed then speed = min_speed end
        end

        -- Kickstart
        local start_t = storage.get('solar_start')
        if pump_was_off then
            storage.set('solar_start', now)
            speed = 100
        elseif start_t and (now - start_t) < (grp.getvalue(ga_kickstart_sec) or 5) then
            speed = 100
        end
    else
        storage.set('solar_start', nil)
        speed = 0
        run_relay = false
    end
end

-- --- FINAL OUTPUTS ---
grp.write(ga_pump_run, run_relay)
grp.write(ga_pump_speed, speed)
if target_boiler == 1 then grp.write(ga_valve_pos, false)
elseif target_boiler == 2 then grp.write(ga_valve_pos, true) end
Reply


Messages In This Thread
Solar Heating Controller - by KoBra - 25.02.2026, 13:28

Forum Jump: