Skip to content

Complex Sequence to test #28

@bbartling

Description

@bbartling
You are designing BAS control logic for an AHU economizer with heating and mechanical cooling. Build a clear state machine + control loops (pseudo-code or structured steps). Keep it implementable on Niagara 4 / generic BAS. No psychrometrics—use only outside-air dry-bulb and outside-air dew point. Provide final outputs:
- heat_valve (0–100%)
- cool_valve (0–100%)
- econ_damper (0–100%)

## Objective
Maintain Supply Air Temperature (SAT) at setpoint. Allow free cooling (economizer) when outside conditions are suitable. In free cooling modes, **no heating is allowed**, but **additional mechanical cooling IS allowed** if econ alone can’t meet SAT.

## Inputs (read-only unless noted)
- OAT: Outside Air Temperature (°F)
- OADP: Outside Air Dew Point (°F)
- SAT: Supply Air Temperature (°F)
- SAT_SP: Supply Air Temperature Setpoint (°F)
- HEAT_SP: Heating activation temperature setpoint (°F)  (typ. SAT_SP - heat_db)
- COOL_SP: Cooling activation temperature setpoint (°F)  (typ. SAT_SP + cool_db)
- free_cool_enable_oat (°F)          # enable threshold (e.g., ≤ 60°F)
- free_cool_disable_oat (°F)         # disable threshold (e.g., ≥ 65°F)
- free_cool_max_dewpoint (°F)        # dew point must be ≤ this to allow econ (e.g., 55°F)
- hysteresis_db (°F)                  # (e.g., 2°F)
- mech_cool_add_threshold (°F)        # SAT error above which to add mech cooling in free-cool (e.g., +1.5°F)
- min_off_time_s (sec)                # anti-short-cycle timer for mode changes (e.g., 300s)
- sample_time_s (sec)                 # loop execution period (e.g., 2s)

## Writable setpoints
- min_oa_enable_status (bool, writable)  # when TRUE, enforce minimum OA damper outside of free cooling
- min_oa_damper (%, writable)            # default 20 (%)

## Outputs (to compute each loop)
- heat_valve 0–100%   # driven ONLY by the heating PID and ONLY in HEAT state
- cool_valve 0–100%   # driven by cooling PID when in FREE_COOL_PLUS_MECH or MECH_COOL
- econ_damper 0–100%  # modulated by economizer logic; respects minimum OA rules

## States (mutually exclusive)
1) HEAT
2) FREE_COOL
3) FREE_COOL_PLUS_MECH  (economizer + mechanical cooling)
4) MECH_COOL

## Mode gating / eligibility
- Free cooling eligibility:
  - ENABLE when (OAT ≤ free_cool_enable_oat) AND (OADP ≤ free_cool_max_dewpoint)
  - DISABLE when (OAT ≥ free_cool_disable_oat OR OADP > free_cool_max_dewpoint + hysteresis_db)
- Heating eligibility:
  - SAT ≤ HEAT_SP (with deadband) and free cooling NOT eligible (or SAT demand clearly heating)
- Mechanical cooling eligibility:
  - SAT ≥ COOL_SP (with deadband). In FREE_COOL, add mech cooling when SAT error > mech_cool_add_threshold.

## OA damper rules
- If min_oa_enable_status == TRUE AND state is NOT FREE_COOL or FREE_COOL_PLUS_MECH:
  - econ_damper := max(econ_damper, min_oa_damper)  # enforce minimum OA outside free-cool modes
- In FREE_COOL or FREE_COOL_PLUS_MECH:
  - econ_damper may open above min_oa_damper as needed by the cooling PID (see below).

## Control loops (anti-windup + clamping required)
- Heating PID (targets SAT_SP):
  - Active ONLY in HEAT state.
  - Output → heat_valve (0–100). Clamp and apply anti-windup.
- Cooling PID (targets SAT_SP):
  - In FREE_COOL: PID drives econ_damper (0–100). cool_valve = 0.
  - In FREE_COOL_PLUS_MECH: 
      * First drive econ_damper toward 100% as needed by PID; 
      * If SAT still > SAT_SP + mech_cool_add_threshold, enable cool_valve via cooling PID (second output or cascaded).
  - In MECH_COOL: econ_damper held at min (if min_oa_enable_status TRUE), otherwise at ventilation min. PID drives cool_valve.
  - Clamp outputs and apply anti-windup.

Suggested default PID tuning placeholders (to be site-tuned):
- Heat PID: Kp=1.2, Ki=0.02, Kd=0.0
- Cool PID (econ & mech): Kp=1.5, Ki=0.03, Kd=0.0

## State selection logic (evaluate in order, respect min_off_time_s)
1) If free-cool eligible:
   - If SAT ≥ COOL_SP:
       -> If econ_damper near max and SAT still too high by > mech_cool_add_threshold, choose FREE_COOL_PLUS_MECH
       -> Else choose FREE_COOL
   - Else if SAT ≤ HEAT_SP:
       -> (Do NOT heat while free-cool eligible) choose FREE_COOL (cooling PID may settle econ closed if SAT below SP)
   - Else:
       -> FREE_COOL (economizer PID maintains SAT_SP)
2) Else if NOT free-cool eligible:
   - If SAT ≤ HEAT_SP -> HEAT
   - Else if SAT ≥ COOL_SP -> MECH_COOL
   - Else -> hold last stable state (no heat/cool valves active; econ at min if required)

## Outputs by state (each loop)
- HEAT:
  - heat_valve := HeatPID(SAT_SP - SAT)
  - cool_valve := 0
  - econ_damper := (min_oa_enable_status ? max(current, min_oa_damper) : ventilation_min)
- FREE_COOL:
  - heat_valve := 0
  - cool_valve := 0
  - econ_damper := CoolPID_as_econ(SAT_SP - SAT)  # 0–100; clamp
- FREE_COOL_PLUS_MECH:
  - heat_valve := 0
  - econ_damper := CoolPID_as_econ(SAT_SP - SAT)  # bias toward opening; allow up to 100%
  - If SAT > SAT_SP + mech_cool_add_threshold:
      cool_valve := CoolPID_as_mech(SAT_SP - SAT)  # 0–100; clamp
    Else:
      cool_valve := 0
- MECH_COOL:
  - heat_valve := 0
  - econ_damper := (min_oa_enable_status ? max(ventilation_min, min_oa_damper) : ventilation_min)
  - cool_valve := CoolPID_as_mech(SAT_SP - SAT)

## Safeties & details
- Mutual exclusion: heat_valve and cool_valve are never >0 simultaneously.
- Apply deadbands: heat_db and cool_db around SAT_SP to avoid hunting.
- Apply min_off_time_s before switching between HEAT and any COOL state, and before enabling mech cooling.
- Bumpless transfer: freeze/reset integral when switching states; ramp outputs over 5–10s.
- Clamp all outputs 0–100 and NaN-guard inputs; on sensor fault, fail to safe (econ at min; heat/cool = 0).

## Deliverables
1) A readable state-machine description with the above rules.
2) Pseudocode implementing the loop, including:
   - state selection with timers/hysteresis
   - two PIDs (heat and cool) with anti-windup
   - computation of heat_valve, cool_valve, econ_damper
3) A concise parameter block for all tunables (setpoints, thresholds, gains, timers).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions