<ide_opened_file>The user opened the file d:\Project\nuvoton\HECO2\doc\Gitea_저장소_접속_및_협업_가이드.md in the IDE. This may or may not be related to the current task.</ide_opened_file>

heco2 소스가 운전모드에 따른 댐퍼 동작이 잘 안되는 것 같아.
ervsimulator와 비교해서 알려줘.


claude claude-code-guide Explore general-purpose Plan statusline-setup

- claude: Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed. (Tools: *)

- claude-code-guide: Use this agent when the user asks questions ("Can Claude...", "Does Claude...", "How do I...") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - API usage, tool use, Anthropic SDK usage. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage. (Tools: Glob, Grep, Read, WebFetch, WebSearch)

- Explore: Read-only search agent for broad fan-out searches — when answering means sweeping many files, directories, or naming conventions and you only need the conclusion, not the file dumps. It reads excerpts rather than whole files, so it locates code; it doesn't review or audit it. Specify search breadth: "medium" for moderate exploration, "very thorough" for multiple locations and naming conventions. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)

- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)

- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)

- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)


- deep-research: Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. - When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., "what car to buy" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.
- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors ("from now on when X", "each time X", "whenever X", "before/after X") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions ("allow X", "add permission", "move permission to"), env vars ("set X=Y"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: "allow npm commands", "add bq permission to global settings", "move permission to user settings", "set DEBUG=true", "when claude stops show X". For simple settings like theme/model, suggest the /config command.
- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: "rebind ctrl+s", "add a chord shortcut", "change the submit key", "customize keybindings".
- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.
- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.
- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.
- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.
- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.
- schedule: Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring cloud agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run ("run this once at 3pm", "remind me to check X tomorrow").
- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.
TRIGGER — read BEFORE opening the target file; don't skip because it "looks like a one-liner" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).
SKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).
- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).
- init: Initialize a new CLAUDE.md file with codebase documentation
- review: Review a pull request
- security-review: Complete a security review of the pending changes on the current branch

deep-research update-config keybindings-help verify code-review simplify fewer-permission-prompts loop schedule claude-api run init review security-review


HECO2 댐퍼 동작 문제 분석 및 비교


댐퍼 동작 로직과 ervsimulator를 찾아 비교하겠습니다. 먼저 관련 파일들을 탐색하겠습니다.


No files found


Simulator\DiffuserSimulator\App.xaml
Simulator\DiffuserSimulator\App.xaml.cs
Simulator\DiffuserSimulator\DiffuserSimulator.csproj
Simulator\DiffuserSimulator\MainWindow.xaml
Simulator\DiffuserSimulator\MainWindow.xaml.cs
Simulator\DiffuserSimulator\RoomPanel.xaml
Simulator\DiffuserSimulator\RoomPanel.xaml.cs
Simulator\DiffuserSimulator\SlaveProtocol.cs
Simulator\ERVSimulator\Program\App.xaml
Simulator\ERVSimulator\Program\App.xaml.cs
Simulator\ERVSimulator\Program\ERVSimulator.csproj
Simulator\ERVSimulator\Program\HystWindow.xaml
Simulator\ERVSimulator\Program\HystWindow.xaml.cs
Simulator\ERVSimulator\Program\MainWindow.xaml
Simulator\ERVSimulator\Program\MainWindow.xaml.cs
Simulator\ERVSimulator\Program\Model\AutoLogic.cs
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs
Simulator\ERVSimulator\Program\Model\ErvState.cs
Simulator\ERVSimulator\Program\Model\Modes.cs
Simulator\ERVSimulator\Program\Protocol\ChecksumHelper.cs
Simulator\ERVSimulator\Program\Protocol\DiffuserMasterProtocol.cs
Simulator\ERVSimulator\Program\Protocol\HomeNetProtocol.cs
Simulator\ERVSimulator\Program\Protocol\HoodMasterProtocol.cs
Simulator\ERVSimulator\Program\Protocol\RoomConProtocol.cs
Simulator\ERVSimulator\Program\Protocol\SerialChannel.cs
Simulator\ERVSimulator\Program\VspWindow.xaml
Simulator\ERVSimulator\Program\VspWindow.xaml.cs
Simulator\ERVSimulator\doc\ERV 시뮬레이터 프로그램 구성.pptx
Simulator\ERVSimulator\doc\NRT 사양서_힘펠 Module CTOUMN21-AU0_210524.pdf
Simulator\ERVSimulator\doc\VSP 테이블.pdf
Simulator\ERVSimulator\doc\스테핑모터 핀 매핑.txt
Simulator\HoodSimulator\App.xaml
Simulator\HoodSimulator\App.xaml.cs
Simulator\HoodSimulator\HoodProtocol.cs
Simulator\HoodSimulator\HoodSimulator.csproj
Simulator\HoodSimulator\MainWindow.xaml
Simulator\HoodSimulator\MainWindow.xaml.cs
Simulator\HoodSimulator\사양서.md
Simulator\RJ2_RoomConSimulator\App.xaml
Simulator\RJ2_RoomConSimulator\App.xaml.cs
Simulator\RJ2_RoomConSimulator\MainWindow.xaml
Simulator\RJ2_RoomConSimulator\MainWindow.xaml.cs
Simulator\RJ2_RoomConSimulator\RJ2RoomConSimulator.csproj
Simulator\RJ2_RoomConSimulator\RoomConProtocol.cs
Simulator\RJ2_RoomConSimulator\VspWindow.xaml
Simulator\RJ2_RoomConSimulator\VspWindow.xaml.cs
Simulator\RJ2_RoomConSimulator\사양서.md
Simulator\ERVSimulator\Program\obj\ERVSimulator.csproj.nuget.g.props
Simulator\ERVSimulator\Program\obj\ERVSimulator.csproj.nuget.g.targets
Simulator\HoodSimulator\obj\HoodSimulator.csproj.nuget.g.props
Simulator\HoodSimulator\obj\HoodSimulator.csproj.nuget.g.targets
Simulator\DiffuserSimulator\obj\DiffuserSimulator.csproj.nuget.g.props
Simulator\ERVSimulator\Program\obj\project.assets.json
Simulator\DiffuserSimulator\obj\DiffuserSimulator.csproj.nuget.g.targets
Simulator\RJ2_RoomConSimulator\obj\RJ2RoomConSimulator.csproj.nuget.g.props
Simulator\ERVSimulator\Program\obj\project.nuget.cache
Simulator\HoodSimulator\obj\project.assets.json
Simulator\RJ2_RoomConSimulator\obj\RJ2RoomConSimulator.csproj.nuget.g.targets
Simulator\HoodSimulator\obj\project.nuget.cache
Simulator\DiffuserSimulator\obj\project.assets.json
Simulator\ERVSimulator\Program\obj\ERVSimulator.csproj.nuget.dgspec.json
Simulator\HoodSimulator\obj\HoodSimulator.csproj.nuget.dgspec.json
Simulator\RJ2_RoomConSimulator\obj\project.assets.json
Simulator\DiffuserSimulator\obj\project.nuget.cache
Simulator\RJ2_RoomConSimulator\obj\project.nuget.cache
Simulator\RJ2_RoomConSimulator\obj\RJ2RoomConSimulator.csproj.nuget.dgspec.json
Simulator\DiffuserSimulator\obj\DiffuserSimulator.csproj.nuget.dgspec.json
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator.assets.cache
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator.csproj.AssemblyReference.cache
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator.GeneratedMSBuildEditorConfig.editorconfig
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\.NETCoreApp,Version=v10.0.AssemblyAttributes.cs
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator.AssemblyInfoInputs.cache
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator.AssemblyInfo.cs
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\App.g.cs
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\App.baml
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\MainWindow.baml
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\MainWindow.g.cs
Simulator\HoodSimulator\obj\Debug\net10.0-windows\win-x64\HoodSimulator_MarkupCompile.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator.assets.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator.csproj.AssemblyReference.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator.GeneratedMSBuildEditorConfig.editorconfig
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\.NETCoreApp,Version=v10.0.AssemblyAttributes.cs
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator.AssemblyInfoInputs.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator.AssemblyInfo.cs
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\RJ2RoomConSimulator.assets.cache
Simulator\ERVSimulator\Program\obj\Debug\net10.0-windows\win-x64\ERVSimulator.assets.cache
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\RJ2RoomConSimulator.csproj.AssemblyReference.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\App.baml
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\App.g.cs
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\RJ2RoomConSimulator.GeneratedMSBuildEditorConfig.editorconfig
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\.NETCoreApp,Version=v10.0.AssemblyAttributes.cs
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\MainWindow.g.cs
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\MainWindow.baml
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\RJ2RoomConSimulator.AssemblyInfoInputs.cache
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\RoomPanel.g.cs
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\RoomPanel.baml
Simulator\DiffuserSimulator\obj\Debug\net10.0-windows\win-x64\DiffuserSimulator_MarkupCompile.cache
Simulator\RJ2_RoomConSimulator\obj\Debug\net10.0-windows\win-x64\RJ2RoomConSimulator.AssemblyInfo.cs
Simulator\ERVSimulator\Program\obj\Debug\net10.0-windows\win-x64\ERVSimulator.csproj.AssemblyReference.cache
Simulator\ERVSimulator\Program\obj\Debug\net10.0-windows\win-x64\ERVSimulator.GeneratedMSBuildEditorConfig.editorconfig
(Results are truncated. Consider using a more specific path or pattern.)


heco2 소스가 운전모드에 따른 댐퍼 동작이 잘 안되는 것 같아. ervsimulator와 비교해서 알려줘.


Found 50 files
doc\260617_수정내용.md
TestProgram\PCDashBoard\Protocol\SerialChannel.cs
Source\HECO2\User\My_Homenet.c
Source\FND_display\IAR\settings\rh_roomcon.dni
Source\FND_display\IAR\settings\r_remocon.dni
Source\FND_display\IAR\settings\C3.dni
Simulator\RJ2_RoomConSimulator\사양서.md
Simulator\RJ2_RoomConSimulator\MainWindow.xaml.cs
Simulator\RJ2_RoomConSimulator\RJ2RoomConSimulator.csproj
Simulator\RJ2_RoomConSimulator\RoomConProtocol.cs
Simulator\RJ2_RoomConSimulator\VspWindow.xaml
Simulator\RJ2_RoomConSimulator\VspWindow.xaml.cs
Simulator\HoodSimulator\사양서.md
Simulator\RJ2_RoomConSimulator\App.xaml
Simulator\RJ2_RoomConSimulator\App.xaml.cs
Simulator\RJ2_RoomConSimulator\MainWindow.xaml
Simulator\HoodSimulator\HoodProtocol.cs
Simulator\HoodSimulator\HoodSimulator.csproj
Simulator\HoodSimulator\MainWindow.xaml
Simulator\HoodSimulator\MainWindow.xaml.cs
Simulator\HoodSimulator\App.xaml
Simulator\HoodSimulator\App.xaml.cs
Simulator\ERVSimulator\Program\Protocol\SerialChannel.cs
Simulator\ERVSimulator\Program\VspWindow.xaml
Simulator\ERVSimulator\Program\VspWindow.xaml.cs
Simulator\ERVSimulator\Program\Protocol\DiffuserMasterProtocol.cs
Simulator\ERVSimulator\Program\Protocol\HomeNetProtocol.cs
Simulator\ERVSimulator\Program\Protocol\HoodMasterProtocol.cs
Simulator\ERVSimulator\Program\Protocol\RoomConProtocol.cs
Simulator\ERVSimulator\Program\Model\AutoLogic.cs
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs
Simulator\ERVSimulator\Program\Model\ErvState.cs
Simulator\ERVSimulator\Program\Model\Modes.cs
Simulator\ERVSimulator\Program\Protocol\ChecksumHelper.cs
Simulator\ERVSimulator\Program\HystWindow.xaml
Simulator\ERVSimulator\Program\HystWindow.xaml.cs
Simulator\ERVSimulator\Program\MainWindow.xaml
Simulator\ERVSimulator\Program\MainWindow.xaml.cs
Simulator\ERVSimulator\Program\App.xaml
Simulator\ERVSimulator\Program\App.xaml.cs
Simulator\ERVSimulator\Program\ERVSimulator.csproj
Simulator\DiffuserSimulator\MainWindow.xaml.cs
Simulator\DiffuserSimulator\RoomPanel.xaml
Simulator\DiffuserSimulator\RoomPanel.xaml.cs
Simulator\DiffuserSimulator\SlaveProtocol.cs
Simulator\DiffuserSimulator\App.xaml
Simulator\DiffuserSimulator\App.xaml.cs
Simulator\DiffuserSimulator\DiffuserSimulator.csproj
Simulator\DiffuserSimulator\MainWindow.xaml
CLAUDE.md


ERVSimulator의 댐퍼 로직과 펌웨어 소스를 읽어 비교하겠습니다.


1	using System;
2	using System.Windows.Threading;
3
4	namespace ERVSimulator.Model
5	{
6	    // 펌웨어 [MyMotor.c] Damper_Mode() + Diffuser_Damper_process() 시퀀스를 흉내
7	    // - Cmd 변경 시 18초 시퀀스 트리거 (slot 180 / 120 / 60, 100ms tick)
8	    // - 본체 댐퍼 6개: Run_Mode 에 따라 즉시 목표각 세팅
9	    // - 디퓨저 댐퍼: Memory → Current 슬롯별 복사
10	    // - 팬 PWM: 매 tick ±1 ramp
11	    public class DamperSequencer
12	    {
13	        public ErvState State { get; }
14	        private readonly DispatcherTimer _timer;
15	        private int _diffuserSlot;       // 180..0 카운트다운
16	        private int _seqType;            // 1=on, 2=off, 3=decrease, 4=increase
17	        private int _prevAirVolume;
18	        private bool _pendingSequence;
19
20	        public DamperSequencer(ErvState state)
21	        {
22	            State = state;
23	            _timer = new DispatcherTimer(DispatcherPriority.Normal)
24	            {
25	                Interval = TimeSpan.FromMilliseconds(100)
26	            };
27	            _timer.Tick += OnTick;
28	            _timer.Start();
29	        }
30
31	        // RoomCon/HomeNet 핸들러가 Run_Mode/Fan_Mode 갱신 후 호출
32	        public void NotifyCommandChanged()
33	        {
34	            _pendingSequence = true;
35	        }
36
37	        void OnTick(object? sender, EventArgs e)
38	        {
39	            // ---- Fan_Speed_process() 시작부: VENT && Fan=0 → 정지 진입 ----
40	            int newAirVolume = (State.RunMode != RunMode.Off && State.FanMode != 0) ? State.FanMode : 0;
41
42	            if (_pendingSequence)
43	            {
44	                _pendingSequence = false;
45	                _diffuserSlot = 180;
46	                _seqType = DetermineSeqType(_prevAirVolume, newAirVolume);
47	                _prevAirVolume = newAirVolume;
48	            }
49
50	            // ---- Damper_Mode(Run_Mode) — 본체 댐퍼 즉시 목표각 ----
51	            ApplyBodyDamperMode(EffectiveBodyMode());
52
53	            // ---- Diffuser_Damper_process() — 슬롯 기반 적용 ----
54	            if (_diffuserSlot == 180)
55	            {
56	                if (_seqType == 1 || _seqType == 4)
57	                    CopyMemoryToCurrent(1, 2);
58	                else if (_seqType == 2 || _seqType == 3)
59	                    SetFanTargets(); // 즉시 ramp 시작
60	            }
61	            else if (_diffuserSlot == 120)
62	            {
63	                if (_seqType == 1 || _seqType == 4 || _seqType == 2 || _seqType == 3)
64	                    CopyMemoryToCurrent(3, 4);
65	            }
66	            else if (_diffuserSlot == 60)
67	            {
68	                if (_seqType == 1 || _seqType == 4)
69	                    SetFanTargets();
70	                else if (_seqType == 2 || _seqType == 3)
71	                    CopyMemoryToCurrent(1, 2);
72	            }
73
74	            if (_diffuserSlot > 0) _diffuserSlot--;
75
76	            // ---- 팬 ramp ±1 (펌웨어와 동일) ----
77	            if (State.Fan1Current < State.Fan1Target) State.Fan1Current++;
78	            else if (State.Fan1Current > State.Fan1Target) State.Fan1Current--;
79	            if (State.Fan2Current < State.Fan2Target) State.Fan2Current++;
80	            else if (State.Fan2Current > State.Fan2Target) State.Fan2Current--;
81	        }
82
83	        int DetermineSeqType(int prev, int now)
84	        {
85	            if (prev == 0 && now != 0) return 4; // increase (power on)
86	            if (prev != 0 && now == 0) return 3; // decrease (power off)
87	            if (prev > now) return 3;
88	            return 4;
89	        }
90
91	        RunMode EffectiveBodyMode()
92	        {
93	            // VENT && Fan=0 → 본체 댐퍼는 MODE_OFF 로 진입 (펌웨어 Fan_Speed_process 분기)
94	            if (State.RunMode == RunMode.Off) return RunMode.Off;
95	            if (State.RunMode == RunMode.Ventilation && State.FanMode == 0 &&
96	                State.Fan1Current == 0 && State.Fan2Current == 0)
97	                return RunMode.Off;
98	            return State.RunMode;
99	        }
100
101	        // 펌웨어 Damper_Mode() — MyMotor.c:472
102	        void ApplyBodyDamperMode(RunMode mode)
103	        {
104	            switch (mode)
105	            {
106	                case RunMode.Ventilation:
107	                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
108	                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
109	                    break;
110	                case RunMode.AirClean:
111	                    SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
112	                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 0);
113	                    break;
114	                case RunMode.Bypass:
115	                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 0);
116	                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 105);
117	                    break;
118	                case RunMode.Auto:
119	                    // 펌웨어는 자동 시 공기질에 따라 VENT/AIR 선택. 단순화: VENT 와 동일.
120	                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
121	                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
122	                    break;
123	                case RunMode.Off:
124	                default:
125	                    SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
126	                    SetBody(DamperId.SA, 100); SetBody(DamperId.RA, 0); SetBody(DamperId.AIR, 105);
127	                    break;
128	            }
129	        }
130
131	        void SetBody(DamperId id, int angle) => State.GetDamper(id).TargetAngle = angle;
132
133	        void CopyMemoryToCurrent(int fromRoom, int toRoom)
134	        {
135	            for (int r = fromRoom; r <= toRoom; r++)
136	            {
137	                var room = State.GetRoom(r);
138	                room.CurrentSA = room.MemorySA;
139	                room.CurrentRA = room.MemoryRA;
140	            }
141	        }
142
143	        // 펌웨어 Fan_Speed_Setting(Run_Mode, Fan_Mode) — MyMotor.c:1233
144	        void SetFanTargets()
145	        {
146	            int idx = System.Math.Clamp(State.FanMode, (byte)0, (byte)4);
147	            switch (State.RunMode)
148	            {
149	                case RunMode.Ventilation:
150	                case RunMode.Auto:
151	                    State.Fan1Target = State.FanSAPreset_Vent[idx];
152	                    State.Fan2Target = State.FanEAPreset_Vent[idx];
153	                    break;
154	                case RunMode.Bypass:
155	                    State.Fan1Target = State.FanSAPreset_Bypass[idx];
156	                    State.Fan2Target = State.FanEAPreset_Bypass[idx];
157	                    break;
158	                case RunMode.AirClean:
159	                    State.Fan1Target = State.FanSAPreset_Air[idx];
160	                    State.Fan2Target = State.FanEAPreset_Air[idx];
161	                    break;
162	                default:
163	                    State.Fan1Target = 0;
164	                    State.Fan2Target = 0;
165	                    break;
166	            }
167	        }
168	    }
169	}
170

using System;
using System.Windows.Threading;

namespace ERVSimulator.Model
{
    // 펌웨어 [MyMotor.c] Damper_Mode() + Diffuser_Damper_process() 시퀀스를 흉내
    // - Cmd 변경 시 18초 시퀀스 트리거 (slot 180 / 120 / 60, 100ms tick)
    // - 본체 댐퍼 6개: Run_Mode 에 따라 즉시 목표각 세팅
    // - 디퓨저 댐퍼: Memory → Current 슬롯별 복사
    // - 팬 PWM: 매 tick ±1 ramp
    public class DamperSequencer
    {
        public ErvState State { get; }
        private readonly DispatcherTimer _timer;
        private int _diffuserSlot;       // 180..0 카운트다운
        private int _seqType;            // 1=on, 2=off, 3=decrease, 4=increase
        private int _prevAirVolume;
        private bool _pendingSequence;

        public DamperSequencer(ErvState state)
        {
            State = state;
            _timer = new DispatcherTimer(DispatcherPriority.Normal)
            {
                Interval = TimeSpan.FromMilliseconds(100)
            };
            _timer.Tick += OnTick;
            _timer.Start();
        }

        // RoomCon/HomeNet 핸들러가 Run_Mode/Fan_Mode 갱신 후 호출
        public void NotifyCommandChanged()
        {
            _pendingSequence = true;
        }

        void OnTick(object? sender, EventArgs e)
        {
            // ---- Fan_Speed_process() 시작부: VENT && Fan=0 → 정지 진입 ----
            int newAirVolume = (State.RunMode != RunMode.Off && State.FanMode != 0) ? State.FanMode : 0;

            if (_pendingSequence)
            {
                _pendingSequence = false;
                _diffuserSlot = 180;
                _seqType = DetermineSeqType(_prevAirVolume, newAirVolume);
                _prevAirVolume = newAirVolume;
            }

            // ---- Damper_Mode(Run_Mode) — 본체 댐퍼 즉시 목표각 ----
            ApplyBodyDamperMode(EffectiveBodyMode());

            // ---- Diffuser_Damper_process() — 슬롯 기반 적용 ----
            if (_diffuserSlot == 180)
            {
                if (_seqType == 1 || _seqType == 4)
                    CopyMemoryToCurrent(1, 2);
                else if (_seqType == 2 || _seqType == 3)
                    SetFanTargets(); // 즉시 ramp 시작
            }
            else if (_diffuserSlot == 120)
            {
                if (_seqType == 1 || _seqType == 4 || _seqType == 2 || _seqType == 3)
                    CopyMemoryToCurrent(3, 4);
            }
            else if (_diffuserSlot == 60)
            {
                if (_seqType == 1 || _seqType == 4)
                    SetFanTargets();
                else if (_seqType == 2 || _seqType == 3)
                    CopyMemoryToCurrent(1, 2);
            }

            if (_diffuserSlot > 0) _diffuserSlot--;

            // ---- 팬 ramp ±1 (펌웨어와 동일) ----
            if (State.Fan1Current < State.Fan1Target) State.Fan1Current++;
            else if (State.Fan1Current > State.Fan1Target) State.Fan1Current--;
            if (State.Fan2Current < State.Fan2Target) State.Fan2Current++;
            else if (State.Fan2Current > State.Fan2Target) State.Fan2Current--;
        }

        int DetermineSeqType(int prev, int now)
        {
            if (prev == 0 && now != 0) return 4; // increase (power on)
            if (prev != 0 && now == 0) return 3; // decrease (power off)
            if (prev > now) return 3;
            return 4;
        }

        RunMode EffectiveBodyMode()
        {
            // VENT && Fan=0 → 본체 댐퍼는 MODE_OFF 로 진입 (펌웨어 Fan_Speed_process 분기)
            if (State.RunMode == RunMode.Off) return RunMode.Off;
            if (State.RunMode == RunMode.Ventilation && State.FanMode == 0 &&
                State.Fan1Current == 0 && State.Fan2Current == 0)
                return RunMode.Off;
            return State.RunMode;
        }

        // 펌웨어 Damper_Mode() — MyMotor.c:472
        void ApplyBodyDamperMode(RunMode mode)
        {
            switch (mode)
            {
                case RunMode.Ventilation:
                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
                    break;
                case RunMode.AirClean:
                    SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 0);
                    break;
                case RunMode.Bypass:
                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 0);
                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 105);
                    break;
                case RunMode.Auto:
                    // 펌웨어는 자동 시 공기질에 따라 VENT/AIR 선택. 단순화: VENT 와 동일.
                    SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
                    SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
                    break;
                case RunMode.Off:
                default:
                    SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
                    SetBody(DamperId.SA, 100); SetBody(DamperId.RA, 0); SetBody(DamperId.AIR, 105);
                    break;
            }
        }

        void SetBody(DamperId id, int angle) => State.GetDamper(id).TargetAngle = angle;

        void CopyMemoryToCurrent(int fromRoom, int toRoom)
        {
            for (int r = fromRoom; r <= toRoom; r++)
            {
                var room = State.GetRoom(r);
                room.CurrentSA = room.MemorySA;
                room.CurrentRA = room.MemoryRA;
            }
        }

        // 펌웨어 Fan_Speed_Setting(Run_Mode, Fan_Mode) — MyMotor.c:1233
        void SetFanTargets()
        {
            int idx = System.Math.Clamp(State.FanMode, (byte)0, (byte)4);
            switch (State.RunMode)
            {
                case RunMode.Ventilation:
                case RunMode.Auto:
                    State.Fan1Target = State.FanSAPreset_Vent[idx];
                    State.Fan2Target = State.FanEAPreset_Vent[idx];
                    break;
                case RunMode.Bypass:
                    State.Fan1Target = State.FanSAPreset_Bypass[idx];
                    State.Fan2Target = State.FanEAPreset_Bypass[idx];
                    break;
                case RunMode.AirClean:
                    State.Fan1Target = State.FanSAPreset_Air[idx];
                    State.Fan2Target = State.FanEAPreset_Air[idx];
                    break;
                default:
                    State.Fan1Target = 0;
                    State.Fan2Target = 0;
                    break;
            }
        }
    }
}


1	namespace ERVSimulator.Model
2	{
3	    // HERV 펌웨어 SPEC_MODE_INFO=0x16 (대림사양, 히터X, 바이패스O, 공청X) 기준
4	    // My_define.h:271 #if !((SPEC_MODE_INFO&0x0F)==0x03||==0x06) 분기
5	    public enum RunMode : byte
6	    {
7	        Ventilation = 0,
8	        Auto        = 1,
9	        Bypass      = 2,
10	        AirClean    = 3,
11	        FanTest     = 4,
12	        Off         = 10, // MODE_OFF (MyMotor.c)
13	    }
14
15	    public enum DamperId
16	    {
17	        EA = 1,      // 배기
18	        OA = 2,      // 외기
19	        BYPASS = 3,
20	        SA = 4,      // 급기
21	        RA = 5,      // 환기
22	        AIR = 6,     // 공청
23	    }
24	}
25

namespace ERVSimulator.Model
{
    // HERV 펌웨어 SPEC_MODE_INFO=0x16 (대림사양, 히터X, 바이패스O, 공청X) 기준
    // My_define.h:271 #if !((SPEC_MODE_INFO&0x0F)==0x03||==0x06) 분기
    public enum RunMode : byte
    {
        Ventilation = 0,
        Auto        = 1,
        Bypass      = 2,
        AirClean    = 3,
        FanTest     = 4,
        Off         = 10, // MODE_OFF (MyMotor.c)
    }

    public enum DamperId
    {
        EA = 1,      // 배기
        OA = 2,      // 외기
        BYPASS = 3,
        SA = 4,      // 급기
        RA = 5,      // 환기
        AIR = 6,     // 공청
    }
}


Source\HECO2\User\MyControl.c
Source\HECO2\User\MyMotor.c
Source\HECO2\User\My_Homenet.c
Source\HECO2\User\My_Hood.c
Source\HECO2\User\My_Hood_old.c
Source\HECO2\User\My_RJ2.c
Source\HECO2\User\My_bunbaegi.c
Source\HECO2\User\My_system.c
Source\HECO2\User\main.c
Source\HECO2\User\pwm_duty10000.c


Now let me read the firmware MyMotor.c, focusing on the damper logic.


468:uint8_t Pre_Damper_Mode = 0;
472:void Damper_Mode(uint8_t mmm)
513:    if(Pre_Damper_Mode != mmm)
518:		if(Pre_Damper_Mode == MODE_VENTILATION)
533:    Pre_Damper_Mode = mmm;
565:	Damper_Mode(MODE_OFF);
921:void Fan_Speed_process(void) // 100ms
941:			Damper_Mode(MODE_OFF);
942:            Diffuser_Damper_process(MODE_OFF);
958:			Damper_Mode(MODE_OFF);
964:			Damper_Mode(MODE_VENTILATION);
970:			Damper_Mode(MODE_BYPASS);
976:			Damper_Mode(MODE_AIRCLEAN);
1007:			Damper_Mode(MODE_VENTILATION);
1018:				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1046:			Damper_Mode(MODE_VENTILATION);
1056:				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1083:			Damper_Mode(MODE_BYPASS);
1090:				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1117:			Damper_Mode(MODE_AIRCLEAN);
1124:				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1139:			Damper_Mode(MODE_VENTILATION);
1143:			Damper_Mode(MODE_BYPASS);
1147:			Damper_Mode(MODE_AIRCLEAN);
1161:        Diffuser_Damper_process(Run_Mode);
1237:void Fan_Speed_Setting(uint8_t r_mode, uint8_t f_speed) // Run_Mode, fanspeed
1416:			Damper_Mode(MODE_VENTILATION);
1448:			Damper_Mode(MODE_OFF);
1528:uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
1562:   //         Fan_Speed_Setting(Run_Mode, Fan_Mode);
1583:              Fan_Speed_Setting(Run_Mode, Fan_Mode);
1617:              Fan_Speed_Setting(Run_Mode, Fan_Mode);


Let me read the key firmware functions: Damper_Mode, Fan_Speed_process, and Diffuser_Damper_process.


468	uint8_t Pre_Damper_Mode = 0;
469	volatile uint8_t Step_Status = 0;
470	uint8_t Step_pos = 0;
471
472	void Damper_Mode(uint8_t mmm)
473	{
474
475		switch(mmm)
476		{
477			case MODE_VENTILATION:
478				Damper_Setting(DAMPER_OA,		0); // 90 = close, 0 = open
479				Damper_Setting(DAMPER_EA,	 	0); // 90 = close, 0 = open
480				Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
481				Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
482				Damper_Setting(DAMPER_RA,		70); // 90 = close, 0 = open 3step--reverse
483				Damper_Setting(DAMPER_AIR, 		105); // 90 = close, 0 = open
484
485				break;
486			case MODE_AIRCLEAN:
487				Damper_Setting(DAMPER_OA,		100); // 90 = close, 0 = open
488				Damper_Setting(DAMPER_EA,		100); // 90 = close, 0 = open
489				Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
490				Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
491				Damper_Setting(DAMPER_RA,		140); // 90 = close, 0 = open 3step--reverse
492				Damper_Setting(DAMPER_AIR,		0); // 90 = close, 0 = open
493				break;
494			case MODE_BYPASS:
495				Damper_Setting(DAMPER_OA,		0); // 90 = close, 0 = open
496				Damper_Setting(DAMPER_EA,		0); // 90 = close, 0 = open
497				Damper_Setting(DAMPER_BYPASS,	0); // 90 = close, 0 = open
498				Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
499				Damper_Setting(DAMPER_RA,		140); // 90 = close, 0 = open 3step--reverse
500				Damper_Setting(DAMPER_AIR,		105); // 90 = close, 0 = open
501				break;
502			case MODE_OFF:
503			default:
504				Damper_Setting(DAMPER_OA,		100); // 90 = close, 0 = open
505				Damper_Setting(DAMPER_EA,		100); // 90 = close, 0 = open
506				Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
507				Damper_Setting(DAMPER_SA,		100); // 90 = close, 0 = open
508				Damper_Setting(DAMPER_RA,		0); // 90 = close, 0 = open 3step--reverse
509				Damper_Setting(DAMPER_AIR,		105); // 90 = close, 0 = open
510				break;
511		}
512
513	    if(Pre_Damper_Mode != mmm)
514		{
515			Step_Status = 0;
516
517
518			if(Pre_Damper_Mode == MODE_VENTILATION)
519			{
520				Step_pos = 2;
521			}
522			else if(mmm == MODE_OFF)
523			{
524				Step_pos = 2;
525			}
526			else
527			{
528				Step_pos = 0;
529			}
530
531	    }
532
533	    Pre_Damper_Mode = mmm;
534	}
535
536	// step x = angle*85 / 7.5
537	// angle 90 -> x = 1020
538	uint8_t Step_motor_init(void) // Rev = 0 ������ ,
539	{
540
541		uint16_t i;
542
543		for(i=0; i<7; i++)
544		{
545			Step_dir[i] = 0;
546			Current_Step_Count[i] = 1020;
547			Target_Step_Count[i] = 1020;
548
549		}
550
551		for(i=0; i<1077; i++){Step_M5_Process(1);Step_M6_Process(0);delay_us(2000);} //
552		for(i=0; i<400; i++){Step_M5_Process(1);delay_us(1500);} // EBSN
553		Step_M5_Stop();
554		Step_M6_Stop();
555
556		for(i=0; i<1077; i++){Step_M1_Process(0);Step_M2_Process(0);delay_us(1500);}
557		Step_M1_Stop();
558		Step_M2_Stop();
559
560		for(i=0; i<1077; i++){Step_M3_Process(0);Step_M4_Process(0);delay_us(1500);}
561		Step_M3_Stop();
562		Step_M4_Stop();
563
564
565		Damper_Mode(MODE_OFF);
566
567	    for(i=0; i<7; i++)
568		{
569	          Current_Step_Count[i] = Target_Step_Count[i];
570		}
571	    return 0;
572
573	}
574
575
576	void Damper_Setting(uint8_t damper_num, float angle_degree) // dir = 0 ������
577	{

uint8_t Pre_Damper_Mode = 0;
volatile uint8_t Step_Status = 0;
uint8_t Step_pos = 0;

void Damper_Mode(uint8_t mmm)
{

	switch(mmm)
	{
		case MODE_VENTILATION:
			Damper_Setting(DAMPER_OA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_EA,	 	0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_RA,		70); // 90 = close, 0 = open 3step--reverse
			Damper_Setting(DAMPER_AIR, 		105); // 90 = close, 0 = open

			break;
		case MODE_AIRCLEAN:
			Damper_Setting(DAMPER_OA,		100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_EA,		100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_RA,		140); // 90 = close, 0 = open 3step--reverse
			Damper_Setting(DAMPER_AIR,		0); // 90 = close, 0 = open
			break;
		case MODE_BYPASS:
			Damper_Setting(DAMPER_OA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_EA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_BYPASS,	0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_SA,		0); // 90 = close, 0 = open
			Damper_Setting(DAMPER_RA,		140); // 90 = close, 0 = open 3step--reverse
			Damper_Setting(DAMPER_AIR,		105); // 90 = close, 0 = open
			break;
		case MODE_OFF:
		default:
			Damper_Setting(DAMPER_OA,		100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_EA,		100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_BYPASS,	100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_SA,		100); // 90 = close, 0 = open
			Damper_Setting(DAMPER_RA,		0); // 90 = close, 0 = open 3step--reverse
			Damper_Setting(DAMPER_AIR,		105); // 90 = close, 0 = open
			break;
	}

    if(Pre_Damper_Mode != mmm)
	{
		Step_Status = 0;

		if(Pre_Damper_Mode == MODE_VENTILATION)
		{
			Step_pos = 2;
		}
		else if(mmm == MODE_OFF)
		{
			Step_pos = 2;
		}
		else
		{
			Step_pos = 0;
		}

    }

    Pre_Damper_Mode = mmm;
}

// step x = angle*85 / 7.5
// angle 90 -> x = 1020
uint8_t Step_motor_init(void) // Rev = 0 ������ ,
{

	uint16_t i;

	for(i=0; i<7; i++)
	{
		Step_dir[i] = 0;
		Current_Step_Count[i] = 1020;
		Target_Step_Count[i] = 1020;

	}

	for(i=0; i<1077; i++){Step_M5_Process(1);Step_M6_Process(0);delay_us(2000);} //
	for(i=0; i<400; i++){Step_M5_Process(1);delay_us(1500);} // EBSN
	Step_M5_Stop();
	Step_M6_Stop();

	for(i=0; i<1077; i++){Step_M1_Process(0);Step_M2_Process(0);delay_us(1500);}
	Step_M1_Stop();
	Step_M2_Stop();

	for(i=0; i<1077; i++){Step_M3_Process(0);Step_M4_Process(0);delay_us(1500);}
	Step_M3_Stop();
	Step_M4_Stop();

	Damper_Mode(MODE_OFF);

    for(i=0; i<7; i++)
	{
          Current_Step_Count[i] = Target_Step_Count[i];
	}
    return 0;

}

void Damper_Setting(uint8_t damper_num, float angle_degree) // dir = 0 ������
{


Now the Fan_Speed_process function:


921	void Fan_Speed_process(void) // 100ms
922	{
923	    uint8_t i = 0;
924
925		if((Fan_Mode == 0)&&(Run_Mode == MODE_VENTILATION)&&(RJ_Vsp_Mode != RX_DATA_MODE_VSP)&&(Vsp_Mode[1] != 0x10))
926		{
927			Power_off_process(0);
928
929	        for(i=1; i<7; i++)Diffuser_Power[i] = 0; //////diffuser off
930
931			Target_Fan1_Speed = 0;
932			Target_Fan2_Speed = 0;
933
934			if(Fan1_Speed > Target_Fan1_Speed)Fan1_Speed--;
935			if(Fan2_Speed > Target_Fan2_Speed)Fan2_Speed--;
936
937			PWM_out_process();
938
939			if((Fan1_Speed == 0) && (Fan2_Speed == 0))
940			{
941				Damper_Mode(MODE_OFF);
942	            Diffuser_Damper_process(MODE_OFF);
943			}
944			return;
945		}
946
947
948		//if(Vsp_Mode == VSP_SETTING_MODE)
949		if((Vsp_Mode[1] == 0x10)||(RJ_Vsp_Mode == RX_DATA_MODE_VSP))
950	   {
951			Pre_Vsp_Mode = Vsp_Mode[1];
952
953			if((Vsp_Select >= 0x10)&&(Vsp_Select <= 0x20))
954			{
955
956				Target_Fan1_Speed = 0;
957				Target_Fan2_Speed = 0;
958				Damper_Mode(MODE_OFF);
959				goto PASS1;
960			}
961
962	        if(Vsp_Select <= 4)//vent
963			{
964				Damper_Mode(MODE_VENTILATION);
965	            Target_Fan1_Speed = Test_Fan1_Speed;
966	            Target_Fan2_Speed = Test_Fan2_Speed;
967			}
968	        else if(Vsp_Select <= 5) // bypass
969			{
970				Damper_Mode(MODE_BYPASS);
971	            Target_Fan1_Speed = Test_Fan1_Speed;
972	            Target_Fan2_Speed = Test_Fan2_Speed;
973			}
974	        else if(Vsp_Select <= 9) //air
975			{
976				Damper_Mode(MODE_AIRCLEAN);
977	            Target_Fan1_Speed = Test_Fan1_Speed;
978	            Target_Fan2_Speed = Test_Fan2_Speed;
979			}
980			else if(Vsp_Select == 0x25)
981			{
982				Filter_Rpm_Auto_Cal_Process();
983			}
984
985			goto PASS1;
986		}
987
988		RPM_Auto_Cal_Start = 0;
989
990		if(Run_Mode == MODE_VENTILATION)
991		{
992			if((Pre_Run_Mode != MODE_VENTILATION)||(Power_On == 0))
993			{
994				Power_On = 1;
995	            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
996				Target_Fan1_Speed = 0; // off
997				Target_Fan2_Speed = 0; // off
998
999				Damper_wait_time = 5; //0.5sec
1000			}
1001			else
1002			{
1003				if((Fan1_Speed != 0)||(Fan2_Speed != 0))
1004				{
1005					if(Damper_wait_time == 5)goto PASS1;
1006				}
1007				Damper_Mode(MODE_VENTILATION);
1008
1009
1010
1011				if(Step_Status != 0x3F)goto PASS1;
1012
1013				if(Damper_wait_time == 1)
1014				{
1015					/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
1016					   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
1017					    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
1018					Fan_Speed_Setting(Run_Mode, Fan_Mode);
1019				}
1020
1021				if(Damper_wait_time)Damper_wait_time--;
1022				else
1023				{
1024					;
1025				}
1026			}
1027		}
1028		else if(Run_Mode == MODE_AUTO)
1029		{
1030
1031			if((Pre_Run_Mode != MODE_AUTO)||(Power_On == 0))
1032			{
1033				Power_On = 1;
1034	            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
1035				Target_Fan1_Speed = 0; // off
1036				Target_Fan2_Speed = 0; // off
1037
1038				Damper_wait_time = 5; //0.5sec
1039			}
1040			else
1041			{
1042				if((Fan1_Speed != 0)||(Fan2_Speed != 0))
1043				{
1044					if(Damper_wait_time == 5)goto PASS1;
1045				}
1046				Damper_Mode(MODE_VENTILATION);
1047
1048
1049				if(Step_Status != 0x3F)goto PASS1;
1050
1051				if(Damper_wait_time == 1)
1052				{
1053					/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
1054					   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
1055					    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
1056					Fan_Speed_Setting(Run_Mode, Fan_Mode);
1057				}
1058
1059				if(Damper_wait_time)Damper_wait_time--;
1060				else
1061				{
1062					;
1063				}
1064			}
1065		}
1066		else if(Run_Mode == MODE_BYPASS)
1067		{
1068			if((Pre_Run_Mode != MODE_BYPASS)||(Power_On == 0))
1069			{
1070				Power_On = 1;
1071	            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
1072				Target_Fan1_Speed = 0; // off
1073				Target_Fan2_Speed = 0; // off
1074
1075				Damper_wait_time = 5; //0.5sec
1076			}
1077			else
1078			{
1079				if((Fan1_Speed != 0)||(Fan2_Speed != 0))
1080				{
1081					if(Damper_wait_time == 5)goto PASS1;
1082				}
1083				Damper_Mode(MODE_BYPASS);
1084
1085				if(Step_Status != 0x3F)goto PASS1;
1086
1087				if(Damper_wait_time == 1)
1088				{
1089					/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
1090					Fan_Speed_Setting(Run_Mode, Fan_Mode);
1091				}
1092
1093				if(Damper_wait_time)Damper_wait_time--;
1094				else
1095				{
1096					;
1097				}
1098			}
1099		}
1100		else if(Run_Mode == MODE_AIRCLEAN)
1101		{
1102			if((Pre_Run_Mode != MODE_AIRCLEAN)||(Power_On == 0))
1103			{
1104				Power_On = 1;
1105	            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
1106				Target_Fan1_Speed = 0; // off
1107				Target_Fan2_Speed = 0; // off
1108
1109				Damper_wait_time = 5; //0.5sec
1110			}
1111			else
1112			{
1113				if((Fan1_Speed != 0)||(Fan2_Speed != 0))
1114				{
1115					if(Damper_wait_time == 5)goto PASS1;
1116				}
1117				Damper_Mode(MODE_AIRCLEAN);
1118
1119				if(Step_Status != 0x3F)goto PASS1;
1120
1121				if(Damper_wait_time == 1)
1122				{
1123					/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
1124					Fan_Speed_Setting(Run_Mode, Fan_Mode);
1125				}
1126
1127				if(Damper_wait_time)Damper_wait_time--;
1128				else
1129				{
1130					;
1131				}
1132			}
1133
1134		}
1135		else if(Run_Mode == MODE_FAN_TEST)
1136		{
1137			if(Vsp_Select <= 4) // vent
1138			{
1139				Damper_Mode(MODE_VENTILATION);
1140			}
1141			else if(Vsp_Select == 5)//bypass
1142			{
1143				Damper_Mode(MODE_BYPASS);
1144			}
1145			else // air 6,7,8
1146			{
1147				Damper_Mode(MODE_AIRCLEAN);
1148			}
1149			Target_Fan1_Speed = Test_Fan1_Speed;
1150			Target_Fan2_Speed = Test_Fan2_Speed;
1151		}
1152
1153	PASS1:
1154
1155	    if((Vsp_Mode[1] == 0x10)||(RJ_Vsp_Mode == RX_DATA_MODE_VSP))
1156	    {
1157	      ;
1158	    }
1159	    else
1160	    {
1161	        Diffuser_Damper_process(Run_Mode);
1162	    }
1163
1164		if(Fan1_Speed > Target_Fan1_Speed)Fan1_Speed--;
1165		else if(Fan1_Speed < Target_Fan1_Speed)Fan1_Speed++;
1166
1167		if(Fan2_Speed > Target_Fan2_Speed)Fan2_Speed--;
1168		else if(Fan2_Speed < Target_Fan2_Speed)Fan2_Speed++;
1169
1170

void Fan_Speed_process(void) // 100ms
{
    uint8_t i = 0;

	if((Fan_Mode == 0)&&(Run_Mode == MODE_VENTILATION)&&(RJ_Vsp_Mode != RX_DATA_MODE_VSP)&&(Vsp_Mode[1] != 0x10))
	{
		Power_off_process(0);

        for(i=1; i<7; i++)Diffuser_Power[i] = 0; //////diffuser off

		Target_Fan1_Speed = 0;
		Target_Fan2_Speed = 0;

		if(Fan1_Speed > Target_Fan1_Speed)Fan1_Speed--;
		if(Fan2_Speed > Target_Fan2_Speed)Fan2_Speed--;

		PWM_out_process();

		if((Fan1_Speed == 0) && (Fan2_Speed == 0))
		{
			Damper_Mode(MODE_OFF);
            Diffuser_Damper_process(MODE_OFF);
		}
		return;
	}

	//if(Vsp_Mode == VSP_SETTING_MODE)
	if((Vsp_Mode[1] == 0x10)||(RJ_Vsp_Mode == RX_DATA_MODE_VSP))
   {
		Pre_Vsp_Mode = Vsp_Mode[1];

		if((Vsp_Select >= 0x10)&&(Vsp_Select <= 0x20))
		{

			Target_Fan1_Speed = 0;
			Target_Fan2_Speed = 0;
			Damper_Mode(MODE_OFF);
			goto PASS1;
		}

        if(Vsp_Select <= 4)//vent
		{
			Damper_Mode(MODE_VENTILATION);
            Target_Fan1_Speed = Test_Fan1_Speed;
            Target_Fan2_Speed = Test_Fan2_Speed;
		}
        else if(Vsp_Select <= 5) // bypass
		{
			Damper_Mode(MODE_BYPASS);
            Target_Fan1_Speed = Test_Fan1_Speed;
            Target_Fan2_Speed = Test_Fan2_Speed;
		}
        else if(Vsp_Select <= 9) //air
		{
			Damper_Mode(MODE_AIRCLEAN);
            Target_Fan1_Speed = Test_Fan1_Speed;
            Target_Fan2_Speed = Test_Fan2_Speed;
		}
		else if(Vsp_Select == 0x25)
		{
			Filter_Rpm_Auto_Cal_Process();
		}

		goto PASS1;
	}

	RPM_Auto_Cal_Start = 0;

	if(Run_Mode == MODE_VENTILATION)
	{
		if((Pre_Run_Mode != MODE_VENTILATION)||(Power_On == 0))
		{
			Power_On = 1;
            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
			Target_Fan1_Speed = 0; // off
			Target_Fan2_Speed = 0; // off

			Damper_wait_time = 5; //0.5sec
		}
		else
		{
			if((Fan1_Speed != 0)||(Fan2_Speed != 0))
			{
				if(Damper_wait_time == 5)goto PASS1;
			}
			Damper_Mode(MODE_VENTILATION);

			if(Step_Status != 0x3F)goto PASS1;

			if(Damper_wait_time == 1)
			{
				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
				   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
				    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
				Fan_Speed_Setting(Run_Mode, Fan_Mode);
			}

			if(Damper_wait_time)Damper_wait_time--;
			else
			{
				;
			}
		}
	}
	else if(Run_Mode == MODE_AUTO)
	{

		if((Pre_Run_Mode != MODE_AUTO)||(Power_On == 0))
		{
			Power_On = 1;
            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
			Target_Fan1_Speed = 0; // off
			Target_Fan2_Speed = 0; // off

			Damper_wait_time = 5; //0.5sec
		}
		else
		{
			if((Fan1_Speed != 0)||(Fan2_Speed != 0))
			{
				if(Damper_wait_time == 5)goto PASS1;
			}
			Damper_Mode(MODE_VENTILATION);

			if(Step_Status != 0x3F)goto PASS1;

			if(Damper_wait_time == 1)
			{
				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
				   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
				    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
				Fan_Speed_Setting(Run_Mode, Fan_Mode);
			}

			if(Damper_wait_time)Damper_wait_time--;
			else
			{
				;
			}
		}
	}
	else if(Run_Mode == MODE_BYPASS)
	{
		if((Pre_Run_Mode != MODE_BYPASS)||(Power_On == 0))
		{
			Power_On = 1;
            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
			Target_Fan1_Speed = 0; // off
			Target_Fan2_Speed = 0; // off

			Damper_wait_time = 5; //0.5sec
		}
		else
		{
			if((Fan1_Speed != 0)||(Fan2_Speed != 0))
			{
				if(Damper_wait_time == 5)goto PASS1;
			}
			Damper_Mode(MODE_BYPASS);

			if(Step_Status != 0x3F)goto PASS1;

			if(Damper_wait_time == 1)
			{
				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
				Fan_Speed_Setting(Run_Mode, Fan_Mode);
			}

			if(Damper_wait_time)Damper_wait_time--;
			else
			{
				;
			}
		}
	}
	else if(Run_Mode == MODE_AIRCLEAN)
	{
		if((Pre_Run_Mode != MODE_AIRCLEAN)||(Power_On == 0))
		{
			Power_On = 1;
            for(i=1; i<7; i++)Diffuser_Power[i] = 1; //////diffuser on
			Target_Fan1_Speed = 0; // off
			Target_Fan2_Speed = 0; // off

			Damper_wait_time = 5; //0.5sec
		}
		else
		{
			if((Fan1_Speed != 0)||(Fan2_Speed != 0))
			{
				if(Damper_wait_time == 5)goto PASS1;
			}
			Damper_Mode(MODE_AIRCLEAN);

			if(Step_Status != 0x3F)goto PASS1;

			if(Damper_wait_time == 1)
			{
				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
				Fan_Speed_Setting(Run_Mode, Fan_Mode);
			}

			if(Damper_wait_time)Damper_wait_time--;
			else
			{
				;
			}
		}

	}
	else if(Run_Mode == MODE_FAN_TEST)
	{
		if(Vsp_Select <= 4) // vent
		{
			Damper_Mode(MODE_VENTILATION);
		}
		else if(Vsp_Select == 5)//bypass
		{
			Damper_Mode(MODE_BYPASS);
		}
		else // air 6,7,8
		{
			Damper_Mode(MODE_AIRCLEAN);
		}
		Target_Fan1_Speed = Test_Fan1_Speed;
		Target_Fan2_Speed = Test_Fan2_Speed;
	}

PASS1:

    if((Vsp_Mode[1] == 0x10)||(RJ_Vsp_Mode == RX_DATA_MODE_VSP))
    {
      ;
    }
    else
    {
        Diffuser_Damper_process(Run_Mode);
    }

	if(Fan1_Speed > Target_Fan1_Speed)Fan1_Speed--;
	else if(Fan1_Speed < Target_Fan1_Speed)Fan1_Speed++;

	if(Fan2_Speed > Target_Fan2_Speed)Fan2_Speed--;
	else if(Fan2_Speed < Target_Fan2_Speed)Fan2_Speed++;


Now the Diffuser_Damper_process function:


1528	uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
1529	{
1530	    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;
1531
1532
1533	    if(Diffuser_mode_change_timer == 0)
1534	    {
1535	        Diffuser_mode_change_timer = 180;//about 24sec
1536
1537	            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
1538	            {
1539	                Diffuser_mode_type = 4;
1540	            }
1541	            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
1542	            {
1543	                Diffuser_mode_type = 3;
1544	            }
1545	            else if(Pre_Total_Air_Volume > Total_Air_Volume) //dicrease
1546	            {
1547	                Diffuser_mode_type = 3;
1548	            }
1549	            else //increase
1550	            {
1551	                Diffuser_mode_type = 4;
1552	            }
1553
1554	        Pre_Total_Air_Volume = Total_Air_Volume;
1555
1556	        Pre_Auto_Mode = Auto_Mode;
1557	    }
1558	    else
1559	    {
1560	        if((Pre_Diffuser_mode == mode)&&(First_Diffuser_excute_flag == 0))
1561	        {
1562	   //         Fan_Speed_Setting(Run_Mode, Fan_Mode);
1563	        }
1564	    }
1565
1566	    if(Diffuser_mode_change_timer == 180)
1567	    {
1568	        switch(Diffuser_mode_type)
1569	        {
1570	            case 1: // power on
1571	            case 4: // mode change - Air_volum increse
1572	              Diffuser_Dmp_Ang_SA[1] = Memory_Diffuser_Dmp_Ang_SA[1];
1573	              Diffuser_Dmp_Ang_RA[1] = Memory_Diffuser_Dmp_Ang_RA[1];
1574	              Diffuser_Dmp_Ang_SA[2] = Memory_Diffuser_Dmp_Ang_SA[2];
1575	              Diffuser_Dmp_Ang_RA[2] = Memory_Diffuser_Dmp_Ang_RA[2];
1576
1577
1578	              //Diffuser_Air_quality[1] = Memory_Diffuser_Air_quality[1];
1579	              //Diffuser_Air_quality[2] = Memory_Diffuser_Air_quality[2];
1580	              break;
1581	            case 2: // power off
1582	            case 3: // mode change - Air_volum dicrese
1583	              Fan_Speed_Setting(Run_Mode, Fan_Mode);
1584	              break;
1585	              default:
1586	              break;
1587	        }
1588	    }
1589	    else if(Diffuser_mode_change_timer == 120)
1590	    {
1591	        switch(Diffuser_mode_type)
1592	        {
1593	            case 1: // power on
1594	            case 4: // mode change - Air_volum increse
1595	            case 2: // power off
1596	            case 3: // mode change - Air_volum dicrese
1597
1598	              Diffuser_Dmp_Ang_SA[3] = Memory_Diffuser_Dmp_Ang_SA[3];
1599	              Diffuser_Dmp_Ang_RA[3] = Memory_Diffuser_Dmp_Ang_RA[3];
1600	              Diffuser_Dmp_Ang_SA[4] = Memory_Diffuser_Dmp_Ang_SA[4];
1601	              Diffuser_Dmp_Ang_RA[4] = Memory_Diffuser_Dmp_Ang_RA[4];
1602
1603
1604	              //Diffuser_Air_quality[3] = Memory_Diffuser_Air_quality[3];
1605	              //Diffuser_Air_quality[4] = Memory_Diffuser_Air_quality[4];
1606	              break;
1607	            default:
1608	              break;
1609	        }
1610	    }
1611	    else if(Diffuser_mode_change_timer == 60)
1612	    {
1613	        switch(Diffuser_mode_type)
1614	        {
1615	            case 1: // power on
1616	            case 4: // mode change - Air_volum increse
1617	              Fan_Speed_Setting(Run_Mode, Fan_Mode);
1618	              break;
1619	            case 2: // power off
1620	            case 3: // mode change - Air_volum dicrese
1621	              Diffuser_Dmp_Ang_SA[1] = Memory_Diffuser_Dmp_Ang_SA[1];
1622	              Diffuser_Dmp_Ang_RA[1] = Memory_Diffuser_Dmp_Ang_RA[1];
1623	              Diffuser_Dmp_Ang_SA[2] = Memory_Diffuser_Dmp_Ang_SA[2];
1624	              Diffuser_Dmp_Ang_RA[2] = Memory_Diffuser_Dmp_Ang_RA[2];
1625
1626	              //Diffuser_Air_quality[1] = Memory_Diffuser_Air_quality[1];
1627	              //Diffuser_Air_quality[2] = Memory_Diffuser_Air_quality[2];
1628	              break;
1629	            default:
1630	              break;
1631	        }
1632	    }
1633	    if(Diffuser_mode_change_timer)Diffuser_mode_change_timer--;
1634
1635	    Pre_Diffuser_mode = mode;
1636
1637	    return(Diffuser_mode_change_timer);
1638
1639	}
1640

uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if(Diffuser_mode_change_timer == 0)
    {
        Diffuser_mode_change_timer = 180;//about 24sec

            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
            {
                Diffuser_mode_type = 4;
            }
            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
            {
                Diffuser_mode_type = 3;
            }
            else if(Pre_Total_Air_Volume > Total_Air_Volume) //dicrease
            {
                Diffuser_mode_type = 3;
            }
            else //increase
            {
                Diffuser_mode_type = 4;
            }

        Pre_Total_Air_Volume = Total_Air_Volume;

        Pre_Auto_Mode = Auto_Mode;
    }
    else
    {
        if((Pre_Diffuser_mode == mode)&&(First_Diffuser_excute_flag == 0))
        {
   //         Fan_Speed_Setting(Run_Mode, Fan_Mode);
        }
    }

    if(Diffuser_mode_change_timer == 180)
    {
        switch(Diffuser_mode_type)
        {
            case 1: // power on
            case 4: // mode change - Air_volum increse
              Diffuser_Dmp_Ang_SA[1] = Memory_Diffuser_Dmp_Ang_SA[1];
              Diffuser_Dmp_Ang_RA[1] = Memory_Diffuser_Dmp_Ang_RA[1];
              Diffuser_Dmp_Ang_SA[2] = Memory_Diffuser_Dmp_Ang_SA[2];
              Diffuser_Dmp_Ang_RA[2] = Memory_Diffuser_Dmp_Ang_RA[2];

              //Diffuser_Air_quality[1] = Memory_Diffuser_Air_quality[1];
              //Diffuser_Air_quality[2] = Memory_Diffuser_Air_quality[2];
              break;
            case 2: // power off
            case 3: // mode change - Air_volum dicrese
              Fan_Speed_Setting(Run_Mode, Fan_Mode);
              break;
              default:
              break;
        }
    }
    else if(Diffuser_mode_change_timer == 120)
    {
        switch(Diffuser_mode_type)
        {
            case 1: // power on
            case 4: // mode change - Air_volum increse
            case 2: // power off
            case 3: // mode change - Air_volum dicrese

              Diffuser_Dmp_Ang_SA[3] = Memory_Diffuser_Dmp_Ang_SA[3];
              Diffuser_Dmp_Ang_RA[3] = Memory_Diffuser_Dmp_Ang_RA[3];
              Diffuser_Dmp_Ang_SA[4] = Memory_Diffuser_Dmp_Ang_SA[4];
              Diffuser_Dmp_Ang_RA[4] = Memory_Diffuser_Dmp_Ang_RA[4];

              //Diffuser_Air_quality[3] = Memory_Diffuser_Air_quality[3];
              //Diffuser_Air_quality[4] = Memory_Diffuser_Air_quality[4];
              break;
            default:
              break;
        }
    }
    else if(Diffuser_mode_change_timer == 60)
    {
        switch(Diffuser_mode_type)
        {
            case 1: // power on
            case 4: // mode change - Air_volum increse
              Fan_Speed_Setting(Run_Mode, Fan_Mode);
              break;
            case 2: // power off
            case 3: // mode change - Air_volum dicrese
              Diffuser_Dmp_Ang_SA[1] = Memory_Diffuser_Dmp_Ang_SA[1];
              Diffuser_Dmp_Ang_RA[1] = Memory_Diffuser_Dmp_Ang_RA[1];
              Diffuser_Dmp_Ang_SA[2] = Memory_Diffuser_Dmp_Ang_SA[2];
              Diffuser_Dmp_Ang_RA[2] = Memory_Diffuser_Dmp_Ang_RA[2];

              //Diffuser_Air_quality[1] = Memory_Diffuser_Air_quality[1];
              //Diffuser_Air_quality[2] = Memory_Diffuser_Air_quality[2];
              break;
            default:
              break;
        }
    }
    if(Diffuser_mode_change_timer)Diffuser_mode_change_timer--;

    Pre_Diffuser_mode = mode;

    return(Diffuser_mode_change_timer);

}


HECO2 댐퍼 동작 문제 분석 및 비교


Let me check where `Pre_Run_Mode`, `Total_Air_Volume`, and `Step_Status` are set, plus the AUTO mode air-quality branch.


469:volatile uint8_t Step_Status = 0;
515:		Step_Status = 0;
628:					Step_Status |= 1;
665:					Step_Status |= 2;
705:					Step_Status |= 4;
742:					Step_Status |= 8;
776:					Step_Status |= 0x10;
812:					Step_Status |= 0x20;
863:uint8_t Pre_Run_Mode = 100;
992:		if((Pre_Run_Mode != MODE_VENTILATION)||(Power_On == 0))
1031:		if((Pre_Run_Mode != MODE_AUTO)||(Power_On == 0))
1068:		if((Pre_Run_Mode != MODE_BYPASS)||(Power_On == 0))
1102:		if((Pre_Run_Mode != MODE_AIRCLEAN)||(Power_On == 0))
1174:	Pre_Run_Mode = Run_Mode;
1520:uint8_t Total_Air_Volume = 0, Pre_Total_Air_Volume = 0;
1527:uint8_t First_Diffuser_excute_flag = 0;
1537:            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
1541:            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
1554:        Pre_Total_Air_Volume = Total_Air_Volume;
1560:        if((Pre_Diffuser_mode == mode)&&(First_Diffuser_excute_flag == 0))


Source\HECO2\User\MyMotor.c:1520:uint8_t Total_Air_Volume = 0, Pre_Total_Air_Volume = 0;
Source\HECO2\User\MyMotor.c:1523:uint16_t Diffuser_mode_change_timer = 0;
Source\HECO2\User\MyMotor.c:1524:uint8_t Diffuser_mode_type = 0;
Source\HECO2\User\MyMotor.c:1533:    if(Diffuser_mode_change_timer == 0)
Source\HECO2\User\MyMotor.c:1535:        Diffuser_mode_change_timer = 180;//about 24sec
Source\HECO2\User\MyMotor.c:1537:            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
Source\HECO2\User\MyMotor.c:1539:                Diffuser_mode_type = 4;
Source\HECO2\User\MyMotor.c:1541:            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
Source\HECO2\User\MyMotor.c:1543:                Diffuser_mode_type = 3;
Source\HECO2\User\MyMotor.c:1545:            else if(Pre_Total_Air_Volume > Total_Air_Volume) //dicrease
Source\HECO2\User\MyMotor.c:1547:                Diffuser_mode_type = 3;
Source\HECO2\User\MyMotor.c:1551:                Diffuser_mode_type = 4;
Source\HECO2\User\MyMotor.c:1554:        Pre_Total_Air_Volume = Total_Air_Volume;
Source\HECO2\User\MyMotor.c:1566:    if(Diffuser_mode_change_timer == 180)
Source\HECO2\User\MyMotor.c:1568:        switch(Diffuser_mode_type)
Source\HECO2\User\MyMotor.c:1589:    else if(Diffuser_mode_change_timer == 120)
Source\HECO2\User\MyMotor.c:1591:        switch(Diffuser_mode_type)
Source\HECO2\User\MyMotor.c:1611:    else if(Diffuser_mode_change_timer == 60)
Source\HECO2\User\MyMotor.c:1613:        switch(Diffuser_mode_type)
Source\HECO2\User\MyMotor.c:1633:    if(Diffuser_mode_change_timer)Diffuser_mode_change_timer--;
Source\HECO2\User\MyMotor.c:1637:    return(Diffuser_mode_change_timer);
Source\HECO2\User\main.c:173:            Total_Air_Volume = Air_Quality_damper_process();  // 260520 사양 자동 동작로직(집중/분산) - 1s 주기
Source\HECO2\User\My_Homenet.c:291:                            /* 즉시 반영 : 풍량 단수(Total_Air_Volume)가 안 바뀌는 모드전환
Source\HECO2\User\My_define.h:491:extern uint8_t Total_Air_Volume, Pre_Total_Air_Volume;


`Total_Air_Volume` is only set in main.c via `Air_Quality_damper_process()`. Let me look at both that and the Homenet comment about mode change.


270	                if(pl[0] == 0) Power_On = 0;
271	                else
272	                {
273	                    Power_On = 1;
274	                    switch(pl[0])
275	                    {
276	                        case 1: Set_Run_Mode = MODE_VENTILATION; break;
277	                        case 2: Set_Run_Mode = MODE_AUTO;        break;
278	                        case 3: Set_Run_Mode = MODE_AIRCLEAN;    break;
279	                        case 4: Set_Run_Mode = MODE_BYPASS;      break;
280	                        default: result = 1; break;
281	                    }
282	                    if(result == 0)
283	                    {
284	                        Run_Mode = Set_Run_Mode;
285	                        Command_request_type |= TYPE_MODE;   /* 룸컨(RJ2)이 새 모드 푸시 */
286	                        /* 운전모드 전환 시 풍량 1단 (자동 제외 - 자동은 부하점수로 결정) */
287	                        if(Set_Run_Mode != MODE_AUTO)
288	                        {
289	                            Set_Fan_Mode = Fan_Mode = 1;
290	                            Command_request_type |= TYPE_FAN_SPEED;
291	                            /* 즉시 반영 : 풍량 단수(Total_Air_Volume)가 안 바뀌는 모드전환
292	                               (예: 환기1단→공청1단)에서도 새 모드 VSP가 걸리도록 CTRL_FAN 과 동일하게 직접 호출 */
293	                            Fan_Speed_Setting(Run_Mode, Fan_Mode);
294	                        }
295	                    }
296	                }
297	                /* 모드 전환 시 수동 댐퍼 해제 → 새 모드는 기본(전실 개방)에서 시작 */
298	                for(i = 1; i <= 4; i++) Diffuser_Damper_Manual[i] = 0;
299	            } else result = 1;
300	            break;
301	        case 0x03:  /* CTRL_FAN [speed] */
302	            if(len >= 1)
303	            {
304	                Set_Fan_Mode = Fan_Mode = pl[0];
305	                Command_request_type |= TYPE_FAN_SPEED;   /* 룸컨(RJ2)이 새 풍량 푸시 */
306	                if(Run_Mode != MODE_AUTO) Fan_Speed_Setting(Run_Mode, Fan_Mode);   /* 즉시 반영(자동은 부하점수로 결정) */
307	            }
308	            else result = 1;
309	            break;
310	        case 0x04:  /* CTRL_SUBMODE [type][onoff] 1수면/2조리/3회복 */
311	            if(len >= 2)
312	            {
313	                if(pl[0] == 1)      Ext_Run_Mode = pl[1] ? 4 : 0;
314	                else if(pl[0] == 2) Hood_YeunDong_Enable = pl[1] ? 1 : 0;
315	                else if(pl[0] == 3){ Ext_Run_Mode = pl[1] ? 1 : 0; Ext_Select_Room = 2; }
316	                else result = 1;
317	                /* 부가모드 변경 시 수동 댐퍼 해제 (수동 댐퍼는 환기/공청/바이패스에서만) */
318	                if(result == 0) for(i = 1; i <= 4; i++) Diffuser_Damper_Manual[i] = 0;
319	            } else result = 1;
320	            break;
321	        case 0x05:  /* CTRL_HOOD [onoff] */
322	            if(len >= 1) Hood_YeunDong_Enable = pl[0] ? 1 : 0; else result = 1;
323	            break;
324	        case 0x06:  /* CTRL_HYST_PRESET [preset] */
325	            if(len >= 1 && pl[0] < 3) Hyst_Preset = pl[0]; else result = 1;
326	            break;
327	        case 0x07:  /* CTRL_HYST_VALUE [preset][pm25][pm10][voc][co2] u16 BE */
328	            if(len >= 9 && pl[0] < 3)
329	            {

                if(pl[0] == 0) Power_On = 0;
                else
                {
                    Power_On = 1;
                    switch(pl[0])
                    {
                        case 1: Set_Run_Mode = MODE_VENTILATION; break;
                        case 2: Set_Run_Mode = MODE_AUTO;        break;
                        case 3: Set_Run_Mode = MODE_AIRCLEAN;    break;
                        case 4: Set_Run_Mode = MODE_BYPASS;      break;
                        default: result = 1; break;
                    }
                    if(result == 0)
                    {
                        Run_Mode = Set_Run_Mode;
                        Command_request_type |= TYPE_MODE;   /* 룸컨(RJ2)이 새 모드 푸시 */
                        /* 운전모드 전환 시 풍량 1단 (자동 제외 - 자동은 부하점수로 결정) */
                        if(Set_Run_Mode != MODE_AUTO)
                        {
                            Set_Fan_Mode = Fan_Mode = 1;
                            Command_request_type |= TYPE_FAN_SPEED;
                            /* 즉시 반영 : 풍량 단수(Total_Air_Volume)가 안 바뀌는 모드전환
                               (예: 환기1단→공청1단)에서도 새 모드 VSP가 걸리도록 CTRL_FAN 과 동일하게 직접 호출 */
                            Fan_Speed_Setting(Run_Mode, Fan_Mode);
                        }
                    }
                }
                /* 모드 전환 시 수동 댐퍼 해제 → 새 모드는 기본(전실 개방)에서 시작 */
                for(i = 1; i <= 4; i++) Diffuser_Damper_Manual[i] = 0;
            } else result = 1;
            break;
        case 0x03:  /* CTRL_FAN [speed] */
            if(len >= 1)
            {
                Set_Fan_Mode = Fan_Mode = pl[0];
                Command_request_type |= TYPE_FAN_SPEED;   /* 룸컨(RJ2)이 새 풍량 푸시 */
                if(Run_Mode != MODE_AUTO) Fan_Speed_Setting(Run_Mode, Fan_Mode);   /* 즉시 반영(자동은 부하점수로 결정) */
            }
            else result = 1;
            break;
        case 0x04:  /* CTRL_SUBMODE [type][onoff] 1수면/2조리/3회복 */
            if(len >= 2)
            {
                if(pl[0] == 1)      Ext_Run_Mode = pl[1] ? 4 : 0;
                else if(pl[0] == 2) Hood_YeunDong_Enable = pl[1] ? 1 : 0;
                else if(pl[0] == 3){ Ext_Run_Mode = pl[1] ? 1 : 0; Ext_Select_Room = 2; }
                else result = 1;
                /* 부가모드 변경 시 수동 댐퍼 해제 (수동 댐퍼는 환기/공청/바이패스에서만) */
                if(result == 0) for(i = 1; i <= 4; i++) Diffuser_Damper_Manual[i] = 0;
            } else result = 1;
            break;
        case 0x05:  /* CTRL_HOOD [onoff] */
            if(len >= 1) Hood_YeunDong_Enable = pl[0] ? 1 : 0; else result = 1;
            break;
        case 0x06:  /* CTRL_HYST_PRESET [preset] */
            if(len >= 1 && pl[0] < 3) Hyst_Preset = pl[0]; else result = 1;
            break;
        case 0x07:  /* CTRL_HYST_VALUE [preset][pm25][pm10][voc][co2] u16 BE */
            if(len >= 9 && pl[0] < 3)
            {


Now let me look at `Air_Quality_damper_process()` which sets `Total_Air_Volume`, the driver for the diffuser sequence.


Found 3 files
Source\HECO2\User\main.c
Source\HECO2\User\My_define.h
Source\HECO2\User\My_system.c


380:    return;
429:            return;
580:			return;
599:			return;
662:        return b->value - a->value;
664:        return a->name - b->name; // 값이 같으면 이름 순으로
736:    return lv;
742:    if (score == 0)  return 0;
743:    if (score <= 4)  return 1;
744:    if (score <= 8)  return 2;
745:    if (score <= 12) return 3;
746:    return 4;
749:uint8_t Air_Quality_damper_process(void)
1173:    return(Tmp_Air_Volume);
1186:    return(0);
1245:    return(0);


749	uint8_t Air_Quality_damper_process(void)
750	{
751	    uint8_t Tmp_Air_Volume = 0;
752	    uint8_t room_CV_quality = 0, room_CVP_quality = 0;
753	    uint8_t Room_Num = 0;
754
755
756	    Tmp_Air_Volume = 0;
757
758	    /* === 공기질 등급(Room_Level) : 표시·자동판정 공용 → 모드(시나리오 포함)와 무관하게 매 틱 갱신 ===
759	       시나리오 모드(안심회복/집중청정/스마트수면/쾌적조리)가 goto PASS_VOLUME 로 아래 메인 루프를
760	       건너뛰어 등급이 멈춰(스마트수면 등에서 대시보드 등급 불일치) → 등급 계산만 앞으로 빼서 항상 수행. */
761	    for(Room_Num = 1; Room_Num < 6; Room_Num++)
762	    {
763	        uint8_t lc, lp25, lp10, lvc, lvl;
764	        lc   = sensor_level(SEN66_CO2_value[Room_Num],            Co2_Thr[Hyst_Preset],  Co2_Db[Hyst_Preset],  Prev_CO2_Lv[Room_Num]);
765	        lp25 = sensor_level(SEN66_pm2p5[Room_Num],                Pm25_Thr[Hyst_Preset], Pm25_Db[Hyst_Preset], Prev_PM25_Lv[Room_Num]);
766	        lp10 = sensor_level(SEN66_pm10p0[Room_Num],               Pm10_Thr[Hyst_Preset], Pm10_Db[Hyst_Preset], Prev_PM10_Lv[Room_Num]);
767	        lvc  = sensor_level((uint16_t)SEN66_VOC_value[Room_Num],  Voc_Thr[Hyst_Preset],  Voc_Db[Hyst_Preset],  Prev_VOC_Lv[Room_Num]);
768	        Prev_CO2_Lv[Room_Num]=lc; Prev_PM25_Lv[Room_Num]=lp25; Prev_PM10_Lv[Room_Num]=lp10; Prev_VOC_Lv[Room_Num]=lvc;
769	        lvl = lc;
770	        if(lp25 > lvl) lvl = lp25;
771	        if(lp10 > lvl) lvl = lp10;
772	        if(lvc  > lvl) lvl = lvc;
773	        Room_Level[Room_Num]      = lvl;
774	        ROOM_air_volume[Room_Num] = lvl;
775	    }
776
777	    if(Force_Damper_Mode == 1)
778	    {
779	        if(Damper_Status_Display & 0x01){Memory_Diffuser_Dmp_Ang_SA[4] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[4] = 0;};
780	        if(Damper_Status_Display & 0x02){Memory_Diffuser_Dmp_Ang_RA[4] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[4] = 0;};
781	        if(Damper_Status_Display & 0x04){Memory_Diffuser_Dmp_Ang_SA[3] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[3] = 0;};
782	        if(Damper_Status_Display & 0x08){Memory_Diffuser_Dmp_Ang_RA[3] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[3] = 0;};
783	        if(Damper_Status_Display & 0x10){Memory_Diffuser_Dmp_Ang_SA[2] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[2] = 0;};
784	        if(Damper_Status_Display & 0x20){Memory_Diffuser_Dmp_Ang_RA[2] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[2] = 0;};
785	        if(Damper_Status_Display & 0x40){Memory_Diffuser_Dmp_Ang_SA[1] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[1] = 0;};
786	        if(Damper_Status_Display & 0x80){Memory_Diffuser_Dmp_Ang_RA[1] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[1] = 0;};
787
788	        if((Memory_Diffuser_Dmp_Ang_SA[4] != 0)||(Memory_Diffuser_Dmp_Ang_RA[4] != 0))Diffuser_Air_quality[4] =  5;
789	        else Diffuser_Air_quality[4] =  0;
790
791	        if((Memory_Diffuser_Dmp_Ang_SA[3] != 0)||(Memory_Diffuser_Dmp_Ang_RA[3] != 0))Diffuser_Air_quality[3] =  5;
792	        else Diffuser_Air_quality[3] =  0;
793
794	        if((Memory_Diffuser_Dmp_Ang_SA[2] != 0)||(Memory_Diffuser_Dmp_Ang_RA[2] != 0))Diffuser_Air_quality[2] =  5;
795	        else Diffuser_Air_quality[2] =  0;
796
797	        if((Memory_Diffuser_Dmp_Ang_SA[1] != 0)||(Memory_Diffuser_Dmp_Ang_RA[1] != 0))Diffuser_Air_quality[1] =  5;
798	        else Diffuser_Air_quality[1] =  0;
799
800	        Tmp_Air_Volume = Set_Fan_Mode;
801
802	        Ext_Run_Mode_Off_Delay = 0;
803
804	        goto PASS_RETURN;
805	    }
806	    else if((Ext_Run_Mode == 1)&&(Pre_Ext_Run_Mode != 1)) //안심회복 모드
807	    {
808	        Memory_Diffuser_Dmp_Ang_SA[6] = 110;
809	        Memory_Diffuser_Dmp_Ang_SA[5] = 110;
810	        Memory_Diffuser_Dmp_Ang_SA[4] = 110;
811	        Memory_Diffuser_Dmp_Ang_SA[3] = 110;
812	        Memory_Diffuser_Dmp_Ang_SA[2] = 110;
813	        Memory_Diffuser_Dmp_Ang_SA[1] = 110;
814
815	        Memory_Diffuser_Dmp_Ang_RA[6] = 0;
816	        Memory_Diffuser_Dmp_Ang_RA[5] = 0;
817	        Memory_Diffuser_Dmp_Ang_RA[4] = 0;
818	        Memory_Diffuser_Dmp_Ang_RA[3] = 0;
819	        Memory_Diffuser_Dmp_Ang_RA[2] = 0;
820	        Memory_Diffuser_Dmp_Ang_RA[1] = 0;
821
822	        Memory_Diffuser_Dmp_Ang_SA[Ext_Select_Room] = 0;
823	        Memory_Diffuser_Dmp_Ang_RA[Ext_Select_Room] = 110;
824
825	        Diffuser_Air_quality[6] = 5;//ON
826	        Diffuser_Air_quality[5] = 5;//ON
827	        Diffuser_Air_quality[4] = 5;//ON
828	        Diffuser_Air_quality[3] = 5;//ON
829	        Diffuser_Air_quality[2] = 5;//ON
830	        Diffuser_Air_quality[1] = 5;//ON
831
832	        Tmp_Air_Volume = 2;//3;/////////////// 2026.1.8 전경선...
833
834	        Command_request_type |= (TYPE_MODE|TYPE_FAN_SPEED);
835
836	        My_Memory_Run_Mode = Run_Mode;
837	        My_Memory_Fan_Mode = Fan_Mode;
838
839	        Set_Run_Mode = MODE_VENTILATION;
840	        Set_Fan_Mode = 2;//3; /////////////// 2026.1.8 전경선...
841
842	        Pre_Ext_Run_Mode = Ext_Run_Mode ;
843	        Pre_Ext_Select_Room = Ext_Select_Room;
844	        goto PASS_VOLUME;
845	    }
846	    else if((Ext_Run_Mode == 2)&&(Pre_Ext_Run_Mode != 2)) //쾌적조리 En
847	    {
848	        Hood_YeunDong_Enable = 1;
849	        Pre_Ext_Run_Mode = Ext_Run_Mode ;
850	        Pre_Ext_Select_Room = Ext_Select_Room;
851	    }
852	    else if((Ext_Run_Mode == 3)&&(Pre_Ext_Run_Mode != 3)) //집중청정 모드
853	    {
854	        Memory_Diffuser_Dmp_Ang_SA[6] = 0;
855	        Memory_Diffuser_Dmp_Ang_SA[5] = 0;
856	        Memory_Diffuser_Dmp_Ang_SA[4] = 0;
857	        Memory_Diffuser_Dmp_Ang_SA[3] = 0;
858	        Memory_Diffuser_Dmp_Ang_SA[2] = 0;
859	        Memory_Diffuser_Dmp_Ang_SA[1] = 0;
860
861	        Memory_Diffuser_Dmp_Ang_RA[6] = 0;
862	        Memory_Diffuser_Dmp_Ang_RA[5] = 0;
863	        Memory_Diffuser_Dmp_Ang_RA[4] = 0;
864	        Memory_Diffuser_Dmp_Ang_RA[3] = 0;
865	        Memory_Diffuser_Dmp_Ang_RA[2] = 0;
866	        Memory_Diffuser_Dmp_Ang_RA[1] = 0;
867
868	        Memory_Diffuser_Dmp_Ang_SA[Ext_Select_Room] = 110;

uint8_t Air_Quality_damper_process(void)
{
    uint8_t Tmp_Air_Volume = 0;
    uint8_t room_CV_quality = 0, room_CVP_quality = 0;
    uint8_t Room_Num = 0;

    Tmp_Air_Volume = 0;

    /* === 공기질 등급(Room_Level) : 표시·자동판정 공용 → 모드(시나리오 포함)와 무관하게 매 틱 갱신 ===
       시나리오 모드(안심회복/집중청정/스마트수면/쾌적조리)가 goto PASS_VOLUME 로 아래 메인 루프를
       건너뛰어 등급이 멈춰(스마트수면 등에서 대시보드 등급 불일치) → 등급 계산만 앞으로 빼서 항상 수행. */
    for(Room_Num = 1; Room_Num < 6; Room_Num++)
    {
        uint8_t lc, lp25, lp10, lvc, lvl;
        lc   = sensor_level(SEN66_CO2_value[Room_Num],            Co2_Thr[Hyst_Preset],  Co2_Db[Hyst_Preset],  Prev_CO2_Lv[Room_Num]);
        lp25 = sensor_level(SEN66_pm2p5[Room_Num],                Pm25_Thr[Hyst_Preset], Pm25_Db[Hyst_Preset], Prev_PM25_Lv[Room_Num]);
        lp10 = sensor_level(SEN66_pm10p0[Room_Num],               Pm10_Thr[Hyst_Preset], Pm10_Db[Hyst_Preset], Prev_PM10_Lv[Room_Num]);
        lvc  = sensor_level((uint16_t)SEN66_VOC_value[Room_Num],  Voc_Thr[Hyst_Preset],  Voc_Db[Hyst_Preset],  Prev_VOC_Lv[Room_Num]);
        Prev_CO2_Lv[Room_Num]=lc; Prev_PM25_Lv[Room_Num]=lp25; Prev_PM10_Lv[Room_Num]=lp10; Prev_VOC_Lv[Room_Num]=lvc;
        lvl = lc;
        if(lp25 > lvl) lvl = lp25;
        if(lp10 > lvl) lvl = lp10;
        if(lvc  > lvl) lvl = lvc;
        Room_Level[Room_Num]      = lvl;
        ROOM_air_volume[Room_Num] = lvl;
    }

    if(Force_Damper_Mode == 1)
    {
        if(Damper_Status_Display & 0x01){Memory_Diffuser_Dmp_Ang_SA[4] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[4] = 0;};
        if(Damper_Status_Display & 0x02){Memory_Diffuser_Dmp_Ang_RA[4] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[4] = 0;};
        if(Damper_Status_Display & 0x04){Memory_Diffuser_Dmp_Ang_SA[3] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[3] = 0;};
        if(Damper_Status_Display & 0x08){Memory_Diffuser_Dmp_Ang_RA[3] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[3] = 0;};
        if(Damper_Status_Display & 0x10){Memory_Diffuser_Dmp_Ang_SA[2] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[2] = 0;};
        if(Damper_Status_Display & 0x20){Memory_Diffuser_Dmp_Ang_RA[2] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[2] = 0;};
        if(Damper_Status_Display & 0x40){Memory_Diffuser_Dmp_Ang_SA[1] = 110;}else{Memory_Diffuser_Dmp_Ang_SA[1] = 0;};
        if(Damper_Status_Display & 0x80){Memory_Diffuser_Dmp_Ang_RA[1] = 110;}else{Memory_Diffuser_Dmp_Ang_RA[1] = 0;};

        if((Memory_Diffuser_Dmp_Ang_SA[4] != 0)||(Memory_Diffuser_Dmp_Ang_RA[4] != 0))Diffuser_Air_quality[4] =  5;
        else Diffuser_Air_quality[4] =  0;

        if((Memory_Diffuser_Dmp_Ang_SA[3] != 0)||(Memory_Diffuser_Dmp_Ang_RA[3] != 0))Diffuser_Air_quality[3] =  5;
        else Diffuser_Air_quality[3] =  0;

        if((Memory_Diffuser_Dmp_Ang_SA[2] != 0)||(Memory_Diffuser_Dmp_Ang_RA[2] != 0))Diffuser_Air_quality[2] =  5;
        else Diffuser_Air_quality[2] =  0;

        if((Memory_Diffuser_Dmp_Ang_SA[1] != 0)||(Memory_Diffuser_Dmp_Ang_RA[1] != 0))Diffuser_Air_quality[1] =  5;
        else Diffuser_Air_quality[1] =  0;

        Tmp_Air_Volume = Set_Fan_Mode;

        Ext_Run_Mode_Off_Delay = 0;

        goto PASS_RETURN;
    }
    else if((Ext_Run_Mode == 1)&&(Pre_Ext_Run_Mode != 1)) //안심회복 모드
    {
        Memory_Diffuser_Dmp_Ang_SA[6] = 110;
        Memory_Diffuser_Dmp_Ang_SA[5] = 110;
        Memory_Diffuser_Dmp_Ang_SA[4] = 110;
        Memory_Diffuser_Dmp_Ang_SA[3] = 110;
        Memory_Diffuser_Dmp_Ang_SA[2] = 110;
        Memory_Diffuser_Dmp_Ang_SA[1] = 110;

        Memory_Diffuser_Dmp_Ang_RA[6] = 0;
        Memory_Diffuser_Dmp_Ang_RA[5] = 0;
        Memory_Diffuser_Dmp_Ang_RA[4] = 0;
        Memory_Diffuser_Dmp_Ang_RA[3] = 0;
        Memory_Diffuser_Dmp_Ang_RA[2] = 0;
        Memory_Diffuser_Dmp_Ang_RA[1] = 0;

        Memory_Diffuser_Dmp_Ang_SA[Ext_Select_Room] = 0;
        Memory_Diffuser_Dmp_Ang_RA[Ext_Select_Room] = 110;

        Diffuser_Air_quality[6] = 5;//ON
        Diffuser_Air_quality[5] = 5;//ON
        Diffuser_Air_quality[4] = 5;//ON
        Diffuser_Air_quality[3] = 5;//ON
        Diffuser_Air_quality[2] = 5;//ON
        Diffuser_Air_quality[1] = 5;//ON

        Tmp_Air_Volume = 2;//3;/////////////// 2026.1.8 전경선...

        Command_request_type |= (TYPE_MODE|TYPE_FAN_SPEED);

        My_Memory_Run_Mode = Run_Mode;
        My_Memory_Fan_Mode = Fan_Mode;

        Set_Run_Mode = MODE_VENTILATION;
        Set_Fan_Mode = 2;//3; /////////////// 2026.1.8 전경선...

        Pre_Ext_Run_Mode = Ext_Run_Mode ;
        Pre_Ext_Select_Room = Ext_Select_Room;
        goto PASS_VOLUME;
    }
    else if((Ext_Run_Mode == 2)&&(Pre_Ext_Run_Mode != 2)) //쾌적조리 En
    {
        Hood_YeunDong_Enable = 1;
        Pre_Ext_Run_Mode = Ext_Run_Mode ;
        Pre_Ext_Select_Room = Ext_Select_Room;
    }
    else if((Ext_Run_Mode == 3)&&(Pre_Ext_Run_Mode != 3)) //집중청정 모드
    {
        Memory_Diffuser_Dmp_Ang_SA[6] = 0;
        Memory_Diffuser_Dmp_Ang_SA[5] = 0;
        Memory_Diffuser_Dmp_Ang_SA[4] = 0;
        Memory_Diffuser_Dmp_Ang_SA[3] = 0;
        Memory_Diffuser_Dmp_Ang_SA[2] = 0;
        Memory_Diffuser_Dmp_Ang_SA[1] = 0;

        Memory_Diffuser_Dmp_Ang_RA[6] = 0;
        Memory_Diffuser_Dmp_Ang_RA[5] = 0;
        Memory_Diffuser_Dmp_Ang_RA[4] = 0;
        Memory_Diffuser_Dmp_Ang_RA[3] = 0;
        Memory_Diffuser_Dmp_Ang_RA[2] = 0;
        Memory_Diffuser_Dmp_Ang_RA[1] = 0;

        Memory_Diffuser_Dmp_Ang_SA[Ext_Select_Room] = 110;


The diffuser sequence trigger is the key area. Let me confirm where the simulator calls `NotifyCommandChanged()`.


Simulator\ERVSimulator\Program\MainWindow.xaml.cs:170:                _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\MainWindow.xaml.cs:182:                _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\MainWindow.xaml.cs:194:                _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\MainWindow.xaml.cs:210:            _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\MainWindow.xaml.cs:399:                _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\Model\AutoLogic.cs:282:                _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\Protocol\RoomConProtocol.cs:118:            _seq.NotifyCommandChanged();
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:15:        private int _diffuserSlot;       // 180..0 카운트다운
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:32:        public void NotifyCommandChanged()
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:45:                _diffuserSlot = 180;
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:54:            if (_diffuserSlot == 180)
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:61:            else if (_diffuserSlot == 120)
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:66:            else if (_diffuserSlot == 60)
Simulator\ERVSimulator\Program\Model\DamperSequencer.cs:74:            if (_diffuserSlot > 0) _diffuserSlot--;
Simulator\ERVSimulator\Program\Protocol\HomeNetProtocol.cs:264:            if (modeChanged) _seq.NotifyCommandChanged();


비교 분석을 마쳤습니다. 핵심 차이를 정리합니다.

## 결론: 디퓨저 댐퍼 시퀀스를 "다시 트리거"하는 방식이 펌웨어와 시뮬레이터가 다릅니다

본체 댐퍼 각도(`Damper_Mode`)는 양쪽이 **완전히 동일**합니다. 문제는 **운전모드가 바뀌었을 때 디퓨저 댐퍼 18초 시퀀스를 언제 다시 시작하느냐**입니다.

### ErvSimulator (의도된 정상 동작)
[DamperSequencer.cs:32-48](Simulator/ERVSimulator/Program/Model/DamperSequencer.cs#L32-L48)
- `NotifyCommandChanged()`가 **명령(모드/풍량)이 바뀔 때마다** 호출됨 → `_pendingSequence = true` → 다음 tick에서 **즉시** `_diffuserSlot = 180`으로 시퀀스 재시작.
- 호출 지점: RoomCon 핸들러, HomeNet 모드변경, UI 버튼, AutoLogic 등 [RoomConProtocol.cs:118](Simulator/ERVSimulator/Program/Protocol/RoomConProtocol.cs#L118), [HomeNetProtocol.cs:264](Simulator/ERVSimulator/Program/Protocol/HomeNetProtocol.cs#L264)
- 즉, **운전모드를 바꾸면 디퓨저 댐퍼 시퀀스가 곧바로 재시작**됩니다.

### 펌웨어 (현재 동작 — 문제)
[MyMotor.c:1528-1557](Source/HECO2/User/MyMotor.c#L1528-L1557)
- 시퀀스 재시작 트리거가 **오직 `Diffuser_mode_change_timer == 0`일 때뿐**입니다. 이 타이머는 함수 내부에서 0이 되면 다시 180으로 자가 리셋되는 **18초 프리런 카운터**입니다.
- `Diffuser_mode_change_timer`는 코드 전체에서 **모드 변경 시 0으로 리셋되는 곳이 전혀 없습니다** (grep 결과 MyMotor.c 내부에서만 등장, My_Homenet.c의 명령 처리부에서 건드리지 않음).
- 게다가 시퀀스 종류(`Diffuser_mode_type`) 판정 기준이 **`Run_Mode`가 아니라 `Total_Air_Volume`(풍량 단수/자동 부하점수)** 입니다. `Total_Air_Volume`은 1초마다 `Air_Quality_damper_process()`에서만 갱신됩니다 [main.c:173](Source/HECO2/User/main.c#L173).

### 그래서 무슨 증상이 나오나
1. **응답 지연**: 운전모드를 바꿔도 디퓨저 댐퍼는 프리런 타이머가 0에 도달할 때까지 **최대 ~18초 늦게** 반응합니다. (시뮬은 즉시)
2. **풍량 단수가 같은 모드전환이 무시됨**: 예를 들어 환기 1단 → 공청 1단처럼 `Total_Air_Volume`이 1→1로 안 바뀌면, 펌웨어는 `prev==now`라 `else → type 4`로만 처리되고 모드 변화 자체는 시퀀스 판정에 반영되지 않습니다. 시뮬레이터는 `NotifyCommandChanged`로 모드전환을 명시적으로 잡아 재시작합니다.

> 참고: 본체 댐퍼(Fan_Speed_process 쪽)는 모드 변경 시 `Pre_Run_Mode != mode` 분기 + `Damper_wait_time`으로 즉시(약 100ms~0.5s) 동작합니다. 따라서 "운전모드에 따라 잘 안 움직인다"는 체감은 **디퓨저(각실) 댐퍼**에서 나오는 것이 거의 확실합니다.

### 제안 수정 방향
펌웨어에 시뮬레이터의 `NotifyCommandChanged()`에 해당하는 **"모드/풍량 변경 시 `Diffuser_mode_change_timer = 0` 강제 리셋"** 을 넣어주면 됩니다. 자연스러운 위치는 My_Homenet.c의 CTRL_MODE/CTRL_FAN 처리부(이미 `Fan_Speed_Setting`을 직접 호출하는 [My_Homenet.c:291-293](Source/HECO2/User/My_Homenet.c#L291-L293), [My_Homenet.c:306](Source/HECO2/User/My_Homenet.c#L306))입니다. 여기서 `Diffuser_mode_change_timer = 0;` 한 줄을 추가하면 다음 100ms tick에 디퓨저 시퀀스가 즉시 재트리거됩니다.

다만 한 가지 결정이 필요합니다 — `Total_Air_Volume`이 안 바뀌는 모드전환(환기1↔공청1)에서도 디퓨저 시퀀스를 강제로 돌릴지 여부입니다. 이건 시퀀스 종류 판정(`Pre_Total_Air_Volume` 비교) 로직도 함께 손봐야 할 수 있습니다.

바로 수정 적용을 진행할까요, 아니면 위 두 함수(`Diffuser_Damper_process` 트리거 + 시퀀스 판정)의 구체적 패치안을 먼저 보여드릴까요? RJ2/분배기 코드는 수정 금지 규칙에 따라 건드리지 않고 `My_Homenet.c`/`MyMotor.c`만 손대겠습니다.


D:\Project\nuvoton\HERV_DL_MH_2nd\Program 여기 소스와 펌웨어를 비교해 봐.


별도 디렉터리의 소스와 현재 펌웨어를 비교하겠습니다. 먼저 해당 폴더 구조를 확인합니다.


이 폴더는 현재 펌웨어와 다른 변종입니다 (`My_Uart.c` 있음 / `My_Homenet.c`·`My_bunbaegi.c` 없음 — DL 각실제어/홈넷 통합 이전의 구 HERV 버전으로 보입니다). 핵심인 댐퍼 로직(MyMotor.c)을 중심으로 차이를 확인하겠습니다.


Bash

cd /d/Project/nuvoton
echo "=== 파일 라인수 비교 (User) ==="
for f in main.c MyControl.c MyMotor.c My_Hood.c My_RJ2.c My_system.c pwm_duty10000.c; do
  a="HECO2/Source/HECO2/User/$f"
  b="HERV_DL_MH_2nd/Program/User/$f"
  if [ -f "$a" ] && [ -f "$b" ]; then
    printf "%-18s HECO2=%-6s MH2nd=%-6s " "$f" "$(wc -l < "$a")" "$(wc -l < "$b")"
    if diff -q "$a" "$b" >/dev/null; then echo "[동일]"; else echo "[차이있음]"; fi
  else
    echo "$f : 한쪽 없음 (a=$( [ -f "$a" ] && echo o || echo x ) b=$( [ -f "$b" ] && echo o || echo x ))"
  fi
done

Compare User source file line counts and identity


=== 파일 라인수 비교 (User) ===
main.c             HECO2=189    MH2nd=175    [차이있음]
MyControl.c        HECO2=692    MH2nd=708    [차이있음]
MyMotor.c          HECO2=1639   MH2nd=1640   [차이있음]
My_Hood.c          HECO2=241    MH2nd=244    [차이있음]
My_RJ2.c           HECO2=779    MH2nd=779    [동일]
My_system.c        HECO2=1247   MH2nd=1428   [차이있음]
pwm_duty10000.c    HECO2=536    MH2nd=536    [동일]
Shell cwd was reset to d:\Project\nuvoton\HECO2


MyMotor.c는 1줄 차이로 거의 동일합니다. 댐퍼 로직이 핵심이니 MyMotor.c부터 정확히 diff 하겠습니다.


cd /d/Project/nuvoton
echo "===== MyMotor.c diff (MH2nd → HECO2) ====="
diff "HERV_DL_MH_2nd/Program/User/MyMotor.c" "HECO2/Source/HECO2/User/MyMotor.c"

Diff MyMotor.c between the two firmwares


HECO2 댐퍼 동작 문제 분석 및 비교


===== MyMotor.c diff (MH2nd → HECO2) =====
19c19
< void Damper_Setting(uint8_t damper_num, float angle_degree); // dir = 0 ������
---
> void Damper_Setting(uint8_t damper_num, float angle_degree); // dir = 0 ������
1015,1016c1015,1018
< 				//if(Target_Fan1_Speed == 0)Target_Fan1_Speed = s_FAN1_VEN_1_DAN; // default
< 				//if(Target_Fan2_Speed == 0)Target_Fan2_Speed = s_FAN2_VEN_1_DAN; // default
---
> 				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
> 				   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
> 				    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
> 				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1051,1052c1053,1056
< 				//if(Target_Fan1_Speed == 0)Target_Fan1_Speed = s_FAN1_VEN_1_DAN; // default
< 				//if(Target_Fan2_Speed == 0)Target_Fan2_Speed = s_FAN2_VEN_1_DAN; // default
---
> 				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영.
> 				   (전환 시 Target 이 0 으로 비워지는데, 풍량 단수가 그대로면 디퓨저 시퀀스가
> 				    재호출하지 않아 팬이 꺼진 채 남던 문제 수정) */
> 				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1085,1086c1089,1090
< 			//	if(Target_Fan1_Speed == 0)Target_Fan1_Speed = s_FAN1_VEN_1_DAN; // default
< 			//	if(Target_Fan2_Speed == 0)Target_Fan2_Speed = s_FAN2_VEN_1_DAN; // default
---
> 				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
> 				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1119,1120c1123,1124
< 			//	if(Target_Fan1_Speed == 0)Target_Fan1_Speed = s_FAN1_VEN_1_DAN; // default
< 			//	if(Target_Fan2_Speed == 0)Target_Fan2_Speed = 0; // default
---
> 				/* 모드전환·댐퍼 정렬 후 새 모드 풍량(VSP) 복원 → 한 번에 반영. */
> 				Fan_Speed_Setting(Run_Mode, Fan_Mode);
1523,1524d1526
< uint8_t Pre_CVnet_CMD = 0;
<
1529,1531c1531,1533
<
<
<     if((Pre_CVnet_CMD != CVnet_CMD)||(Diffuser_mode_change_timer == 0))
---
>
>
>     if(Diffuser_mode_change_timer == 0)
1533,1535d1534
<
<         Pre_CVnet_CMD = CVnet_CMD;
<


결정적인 차이를 찾았습니다. `Diffuser_Damper_process()`의 트리거 조건이 바로 그 부분입니다. CVnet_CMD가 현재 펌웨어에 남아있는지 확인하겠습니다.


No matches found


d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\MyMotor.c:1523:uint8_t Pre_CVnet_CMD = 0;
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\MyMotor.c:1531:    if((Pre_CVnet_CMD != CVnet_CMD)||(Diffuser_mode_change_timer == 0))
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\MyMotor.c:1534:        Pre_CVnet_CMD = CVnet_CMD;
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_define.h:485:extern uint8_t CVnet_CMD;
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1477:uint8_t CVnet_CMD = 0;
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1484:    Tx_CVnet_buff[3] = CVnet_CMD | 0x80;
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1505:        CVnet_CMD = Rx_CVnet_buff[3];
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1507:        if(CVnet_CMD == 0x61)
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1512:        else if(CVnet_CMD == 0x62)// on/off  (Daelim MH 2nd: ERV 3-stage, LED 9, all dampers open/close)
d:\Project\nuvoton\HERV_DL_MH_2nd\Program\User\My_Uart.c:1544:        else if(CVnet_CMD == 0x63)// light dimming (Sub ID 0~9 -> Light_Bright[1..10])


확정됐습니다. **현재 펌웨어(HECO2)에서 디퓨저 댐퍼의 "명령 변경 시 즉시 재시작" 트리거가 통째로 빠졌습니다.** 이게 지난번 "운전모드에 따른 댐퍼 동작이 잘 안된다"의 직접 원인입니다.

## 핵심 차이 — `Diffuser_Damper_process()` 트리거 조건

**HERV_DL_MH_2nd (구 버전, 정상):** [MyMotor.c:1523-1534](D:/Project/nuvoton/HERV_DL_MH_2nd/Program/User/MyMotor.c#L1523-L1534)
```c
uint8_t Pre_CVnet_CMD = 0;
...
if((Pre_CVnet_CMD != CVnet_CMD)||(Diffuser_mode_change_timer == 0))   // ← 명령 변경 시 즉시 재시작
{
    Diffuser_mode_change_timer = 180;
    Pre_CVnet_CMD = CVnet_CMD;
    ...
```

**HECO2 (현재):** [MyMotor.c:1531](Source/HECO2/User/MyMotor.c#L1531)
```c
if(Diffuser_mode_change_timer == 0)   // ← 18초 프리런 타이머만 남음, 명령 트리거 제거됨
{
    Diffuser_mode_change_timer = 180;
    ...
```

**무슨 일이 있었나:** MH2nd는 월패드 명령을 `My_Uart.c`의 `CVnet_CMD`로 받았습니다. HECO2로 오면서 통신부를 `My_Homenet.c`(RJ2/홈넷)로 교체하며 `CVnet_CMD`가 사라졌고, 이때 MyMotor.c를 이식하면서 `Pre_CVnet_CMD != CVnet_CMD` 트리거를 **삭제만 하고 HECO2용 대체 트리거를 넣지 않았습니다.** 그 결과 디퓨저 시퀀스는 18초 자가 타이머가 0이 될 때만 갱신 → 모드 전환 즉시 반응 못 함. (제가 지난 턴에 시뮬레이터의 `NotifyCommandChanged`와 비교해 지적한 바로 그 누락이, 구 펌웨어에는 실제로 존재했다는 증거입니다.)

## 그 외 MyMotor.c 차이 (이건 HECO2가 개선한 쪽)
각 운전모드 분기에서 MH2nd는 팬 기본값 복원이 주석 처리돼 있었는데, HECO2는 `Fan_Speed_Setting(Run_Mode, Fan_Mode)` 호출을 추가했습니다 ([MyMotor.c:1015-1018 등](Source/HECO2/User/MyMotor.c#L1015-L1018)). 즉 **팬 풍량 복원은 고쳤지만 디퓨저 댐퍼 트리거는 빠진** 비대칭 상태입니다.

## 나머지 파일
| 파일 | 상태 | 비고 |
|---|---|---|
| My_RJ2.c, pwm_duty10000.c | 동일 | |
| MyMotor.c | 거의 동일 | 위 트리거 차이가 핵심 |
| main.c / MyControl.c / My_Hood.c / My_system.c | 차이 | HECO2의 DL 각실제어·홈넷 신규 기능 (정상적 분기) |
| My_Uart.c | MH2nd만 존재 | CVnet 월패드 통신 → HECO2는 My_Homenet.c로 대체 |
| My_Homenet.c / My_bunbaegi.c | HECO2만 존재 | 신규 |

## 권장 수정
HECO2에는 `CVnet_CMD`가 없으니, 동일 효과를 내는 **HECO2식 명령-변경 트리거**를 넣으면 됩니다. 이미 `My_Homenet.c`의 CTRL_MODE/CTRL_FAN 처리부가 `Fan_Speed_Setting`을 직접 호출하고 있으니([My_Homenet.c:293](Source/HECO2/User/My_Homenet.c#L293), [306](Source/HECO2/User/My_Homenet.c#L306)), 그 자리에 `Diffuser_mode_change_timer = 0;` 한 줄씩 추가하면 다음 100ms tick에 디퓨저 시퀀스가 즉시 재트리거됩니다. (`Diffuser_mode_change_timer`를 `My_define.h`에 extern 선언 추가 필요 여부만 확인하면 됩니다.)

이 수정을 바로 적용할까요? 적용 시 `My_Homenet.c`와 (필요하면) `My_define.h`만 건드리고, RJ2/분배기 코드는 손대지 않겠습니다. 빌드 확인까지 진행하겠습니다.


RJ2,분배기 코드만 빼고 수정해.


수정 방식을 정했습니다. `My_Homenet.c` 여러 핸들러에 흩뿌리는 대신, 구 펌웨어(MH2nd)의 `Pre_CVnet_CMD != CVnet_CMD` 패턴을 **MyMotor.c 안에서 HECO2식으로 재현**하겠습니다 — 운전모드+풍량을 한 바이트로 합쳐 변경 감지. 이러면 홈넷/RJ2/시나리오 등 모든 명령 경로를 한 곳에서 균일하게 잡고, extern 추가도 불필요합니다. 정확한 편집을 위해 해당 영역을 다시 확인합니다.


1516		}
1517
1518	}
1519
1520	uint8_t Total_Air_Volume = 0, Pre_Total_Air_Volume = 0;
1521
1522	uint8_t Pre_Diffuser_mode = 0;
1523	uint16_t Diffuser_mode_change_timer = 0;
1524	uint8_t Diffuser_mode_type = 0;
1525	uint16_t Get_Air_Volume_Timer = 0;
1526
1527	uint8_t First_Diffuser_excute_flag = 0;
1528	uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
1529	{
1530	    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;
1531
1532
1533	    if(Diffuser_mode_change_timer == 0)
1534	    {
1535	        Diffuser_mode_change_timer = 180;//about 24sec
1536
1537	            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
1538	            {
1539	                Diffuser_mode_type = 4;
1540	            }
1541	            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
1542	            {
1543	                Diffuser_mode_type = 3;
1544	            }
1545	            else if(Pre_Total_Air_Volume > Total_Air_Volume) //dicrease
1546	            {
1547	                Diffuser_mode_type = 3;
1548	            }
1549	            else //increase
1550	            {
1551	                Diffuser_mode_type = 4;
1552	            }
1553
1554	        Pre_Total_Air_Volume = Total_Air_Volume;
1555
1556	        Pre_Auto_Mode = Auto_Mode;
1557	    }

	}

}

uint8_t Total_Air_Volume = 0, Pre_Total_Air_Volume = 0;

uint8_t Pre_Diffuser_mode = 0;
uint16_t Diffuser_mode_change_timer = 0;
uint8_t Diffuser_mode_type = 0;
uint16_t Get_Air_Volume_Timer = 0;

uint8_t First_Diffuser_excute_flag = 0;
uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if(Diffuser_mode_change_timer == 0)
    {
        Diffuser_mode_change_timer = 180;//about 24sec

            if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))
            {
                Diffuser_mode_type = 4;
            }
            else if((Pre_Total_Air_Volume != 0) && (Total_Air_Volume == 0))
            {
                Diffuser_mode_type = 3;
            }
            else if(Pre_Total_Air_Volume > Total_Air_Volume) //dicrease
            {
                Diffuser_mode_type = 3;
            }
            else //increase
            {
                Diffuser_mode_type = 4;
            }

        Pre_Total_Air_Volume = Total_Air_Volume;

        Pre_Auto_Mode = Auto_Mode;
    }


Edit

uint8_t First_Diffuser_excute_flag = 0;
uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if(Diffuser_mode_change_timer == 0)
    {
        Diffuser_mode_change_timer = 180;//about 24sec

uint8_t First_Diffuser_excute_flag = 0;
uint8_t Pre_Diffuser_Cmd = 0xFF;   /* 운전모드+풍량 명령 변경 감지(구 CVnet_CMD 대체) */
uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    /* 운전모드/풍량이 바뀌면 디퓨저 시퀀스를 즉시 재시작.
       (구 펌웨어 HERV_DL_MH_2nd 의 Pre_CVnet_CMD!=CVnet_CMD 트리거를 HECO2 통신구조에 맞게 복원 -
        홈넷·RJ2·시나리오 등 모든 명령 경로를 한 곳에서 균일하게 감지) */
    uint8_t Diffuser_Cmd = (uint8_t)((Run_Mode << 4) | (Fan_Mode & 0x0F));

    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if((Pre_Diffuser_Cmd != Diffuser_Cmd) || (Diffuser_mode_change_timer == 0))
    {
        Pre_Diffuser_Cmd = Diffuser_Cmd;
        Diffuser_mode_change_timer = 180;//about 24sec


The file d:\Project\nuvoton\HECO2\Source\HECO2\User\MyMotor.c has been updated successfully. (file state is current in your context — no need to Read it back)

uint8_t First_Diffuser_excute_flag = 0;
uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if(Diffuser_mode_change_timer == 0)
    {
        Diffuser_mode_change_timer = 180;//about 24sec

uint8_t First_Diffuser_excute_flag = 0;
uint8_t Pre_Diffuser_Cmd = 0xFF;   /* 운전모드+풍량 명령 변경 감지(구 CVnet_CMD 대체) */
uint16_t Diffuser_Damper_process(uint8_t mode)//100ms
{
    /* 운전모드/풍량이 바뀌면 디퓨저 시퀀스를 즉시 재시작.
       (구 펌웨어 HERV_DL_MH_2nd 의 Pre_CVnet_CMD!=CVnet_CMD 트리거를 HECO2 통신구조에 맞게 복원 -
        홈넷·RJ2·시나리오 등 모든 명령 경로를 한 곳에서 균일하게 감지) */
    uint8_t Diffuser_Cmd = (uint8_t)((Run_Mode << 4) | (Fan_Mode & 0x0F));

    if(Focus_Mode_RunTime)Focus_Mode_RunTime--;

    if((Pre_Diffuser_Cmd != Diffuser_Cmd) || (Diffuser_mode_change_timer == 0))
    {
        Pre_Diffuser_Cmd = Diffuser_Cmd;
        Diffuser_mode_change_timer = 180;//about 24sec

+uint8_t Pre_Diffuser_Cmd = 0xFF;   /* 운전모드+풍량 명령 변경 감지(구 CVnet_CMD 대체) */

+    /* 운전모드/풍량이 바뀌면 디퓨저 시퀀스를 즉시 재시작.

+       (구 펌웨어 HERV_DL_MH_2nd 의 Pre_CVnet_CMD!=CVnet_CMD 트리거를 HECO2 통신구조에 맞게 복원 -

+        홈넷·RJ2·시나리오 등 모든 명령 경로를 한 곳에서 균일하게 감지) */

+    uint8_t Diffuser_Cmd = (uint8_t)((Run_Mode << 4) | (Fan_Mode & 0x0F));

-    if(Diffuser_mode_change_timer == 0)

+    if((Pre_Diffuser_Cmd != Diffuser_Cmd) || (Diffuser_mode_change_timer == 0))

+        Pre_Diffuser_Cmd = Diffuser_Cmd;

if((Pre_Total_Air_Volume == 0) && (Total_Air_Volume != 0))


Now let me build to confirm 0 warnings/errors.


Bash

cd /d/Project/nuvoton/HECO2/Source/HECO2 && bash build.sh all 2>&1 | tail -25

Build firmware and check warnings/errors


[CC]  Library/StdDriver/src/i2c.c
[CC]  Library/StdDriver/src/i2s.c
[CC]  Library/StdDriver/src/lcd.c
[CC]  Library/StdDriver/src/pdma.c
[CC]  Library/StdDriver/src/retarget.c
[CC]  Library/StdDriver/src/rtc.c
[CC]  Library/StdDriver/src/sc.c
[CC]  Library/StdDriver/src/scuart.c
[CC]  Library/StdDriver/src/spi.c
[CC]  Library/StdDriver/src/sys.c
[CC]  Library/StdDriver/src/timer.c
[CC]  Library/StdDriver/src/uart.c
[CC]  Library/StdDriver/src/usbd.c
[CC]  Library/StdDriver/src/wdt.c
[CC]  Library/StdDriver/src/wwdt.c
[CC]  Library/Device/Nuvoton/Nano100Series/Source/system_Nano100Series.c
[CC]  Library/Device/Nuvoton/Nano100Series/Source/GCC/_syscalls.c
[AS]  Library/Device/Nuvoton/Nano100Series/Source/GCC/startup_Nano100Series.S
[LD]  build/HERV.elf
[HEX] build/HERV.hex
[BIN] build/HERV.bin

   text	   data	    bss	    dec	    hex	filename
  44008	   1948	   3388	  49344	   c0c0	build/HERV.elf


빌드 성공(경고/오류 0, text 44008B). 경고 여부를 명시적으로 한 번 더 확인합니다.


bash build.sh all 2>&1 | grep -iE "warning|error" | head; echo "---exit grep done---"

Grep build output for warnings/errors


---exit grep done---


경고/오류 0 확인. 작업 기록 규칙에 따라 `doc`에 기록하겠습니다. 오늘 날짜(260618) 파일이 있는지 확인합니다.


No files found


Write

# 260618 디퓨저 댐퍼 — 운전모드 전환 즉시 반영 트리거 복원

## 배경 / 증상
- "운전모드에 따른 댐퍼 동작이 잘 안된다" 보고.
- ErvSimulator(`DamperSequencer`)는 명령 변경 시 `NotifyCommandChanged()`로 18초 디퓨저 시퀀스를 **즉시 재시작**하는데, 현재 HECO2 펌웨어에는 그 트리거가 없었음.
- 본체 댐퍼(`Damper_Mode`)와 팬 풍량은 모드 전환에 즉시 반응하나, **디퓨저(각실) 댐퍼**만 18초 자가 타이머가 0이 될 때까지 갱신 안 됨.

## 원인 (구 펌웨어 비교로 확정)
- `D:\Project\nuvoton\HERV_DL_MH_2nd\Program` (구 HERV, CVnet 월패드/`My_Uart.c` 기반)와 비교.
- 구 버전 `Diffuser_Damper_process()`:
  `if((Pre_CVnet_CMD != CVnet_CMD)||(Diffuser_mode_change_timer == 0))` — **명령 변경 시 즉시 재시작** 트리거 존재.
- HECO2로 이식하며 통신부를 `My_Homenet.c`(RJ2/홈넷)로 교체 → `CVnet_CMD` 소멸 → 트리거를 **삭제만 하고 대체 없이** 남음. 결과적으로 `if(Diffuser_mode_change_timer == 0)` 만 남아 18초 프리런 타이머에만 의존.
- 즉 시뮬레이터 대비 누락된 "명령 변경 → 디퓨저 시퀀스 재시작" 로직이, 구 펌웨어에는 실제로 있었음.

## 변경 파일/내용
- `Source/HECO2/User/MyMotor.c` — `Diffuser_Damper_process()` 트리거 복원.
  - 추가: `uint8_t Pre_Diffuser_Cmd = 0xFF;`
  - 함수 진입부에서 명령 시그니처 합성: `Diffuser_Cmd = (Run_Mode << 4) | (Fan_Mode & 0x0F)`
  - 조건 변경:
    - 전: `if(Diffuser_mode_change_timer == 0)`
    - 후: `if((Pre_Diffuser_Cmd != Diffuser_Cmd) || (Diffuser_mode_change_timer == 0))` + 내부에서 `Pre_Diffuser_Cmd = Diffuser_Cmd;`
- 구 펌웨어의 `Pre_CVnet_CMD != CVnet_CMD` 패턴을 HECO2 통신구조에 맞게 복원한 것. 운전모드+풍량을 한 바이트로 합쳐 변경 감지하므로 홈넷/RJ2/시나리오 등 **모든 명령 경로를 MyMotor.c 한 곳에서 균일하게** 잡음. `My_Homenet.c`/`My_define.h` extern 추가 불필요.

## 핵심 결정
- My_Homenet 핸들러마다 `Diffuser_mode_change_timer = 0`을 흩뿌리는 대신, 함수 내부 자기완결형 명령-감지로 구현(구 CVnet_CMD 의미 그대로 재현, 누락 경로 방지).
- RJ2(`My_RJ2.c`)·분배기(`My_bunbaegi.c`) 코드는 수정하지 않음(검증된 코드).
- 시퀀스 종류(`Diffuser_mode_type`) 판정은 기존대로 `Total_Air_Volume` 기준 유지(구 펌웨어와 동일). 풍량 단수가 같은 모드전환(예: 환기1↔공청1)도 트리거는 걸리며 type 4(increase)로 Memory→Current 재적용.

## 빌드 결과
- `cd SOURCE/HECO2 && bash build.sh all` → 성공, 경고/오류 0.
- text 44008 / data 1948 / bss 3388 (HERV.elf).

## 미해결 / 후속
- 실제 장비/시뮬레이터에서 모드 전환 시 디퓨저 댐퍼 즉시 동작 검증 필요.
- 시나리오(안심회복/집중청정 등)는 Run_Mode/Fan_Mode가 안 바뀌는 경우가 있어 트리거가 안 걸릴 수 있음 — 필요 시 `Ext_Run_Mode`도 시그니처에 포함 검토.