- 發布於
讓家裡的冷氣變聰明:遠端遙控與自動化 DIY

某天滑脆(Threads)看到 @kunlin0813 分享他第一次自己焊接,就把公司的日立冷氣接進了 HomeKit,材料成本不到 100 元。原來滑脆也能滑到能動手做的東西——看完手很癢,決定把自家冷氣也弄上智慧家庭。
我家是 MAXE(萬士益)的分離式冷氣,平常只能拿遙控器在房間裡按。我真正想要的,是兩件原廠做不到的事:人在外面也能遠端開關,以及一些自動化——睡前定時關、關機後自動防霉吹乾這類冷氣預設不會做的事。原廠連網模組又貴、又得繞原廠雲端,於是乾脆自己用一片 ESP32 接管它。
這篇把整個過程記錄下來:硬體怎麼接、ESPHome 怎麼設、過程踩了哪些雷,以及接管之後真正好玩的自動化——包含最後一個看似簡單、結果意外燒腦的小謎題。
為什麼自己做(而不是買原廠模組)
| 原廠連網模組 | 自己 DIY(本專案) | |
|---|---|---|
| 花費 | 整套模組,不便宜 | 幾個零件,便宜得多 |
| 雲端 | 走雲端 | 本地直連,不出門 |
| 自動化 | App 內建、有限 | Home Assistant 全自動化都能用 |
| 生態 | 綁自家 App | Apple Home/Google/Alexa 任選 |
拆開室內機控制盒,主板上印著 KFR-35G——這是美的(Midea)的 OEM 機種。換句話說,這台冷氣的 MCU「母語」就是 Midea 的 UART 協定。剛好 ESPHome 內建、社群成熟的 midea 元件就能直接跟它對話,不用自己逆向協定。先確認自己機種的母語協定,是整件事的第一步。
硬體:一片 ESP32-C3 + 一塊電平轉換板
需要的東西其實很少:
| 物品 | 規格 | 數量 |
|---|---|---|
| ESP32-C3 SuperMini | 開發板 | 1 |
| BSS138 電平轉換板 | 4 通道 | 1 |
| JST XH 2.54mm 帶線母頭 | 4-pin、20cm | 1 |
冷氣控制盒上有個 CN3 WIFI 接頭(JST XH 2.54mm、4-pin),規格是 5V TTL、UART 9600 8N1。問題是 ESP32-C3 的 GPIO 是 3.3V,直接把 5V 灌進去會慢性損壞甚至燒掉,所以中間要墊一塊 BSS138 雙向電平轉換板做 5V ↔ 3.3V 轉換。

關於電容:很多教學會建議在 ESP 的 5V/GND 之間並一顆 470µF 電解電容,避免壓縮機啟動瞬間電壓跌落害 ESP brown-out 重開。我實測沒裝也很穩,所以目前沒裝;如果你的環境會掉電,再並一顆上去(注意極性,接反會爆)。
踩雷①:只有三用電表、沒示波器,分辨 pin 踩了不少雷
CN3 雖然只有 4 根 pin,但要分清楚哪根是 +5V、GND、TX、RX 沒想像中簡單——我手邊只有一支簡易電子三用電表,沒有示波器,只能量靜態電壓去猜。
第一個雷:這台的 pin 排列跟 Midea 慣例完全相反。實測結果是 pin1 = GND、pin4 = +5V(慣例是 pin1 = +5V)。照網路上的 pinout 接,鐵定接錯——一定要自己量。
第二個、也是最坑的一個:量起來最乾淨、紋風不動的那根 5V,其實不是電源。
- 真正的 +5V,量起來會「微微抖動」(有 ripple)——因為冷氣 MCU 在運作、週期性吃電。
- 那根穩定到漂亮的 5V,反而是被 pull-up 的訊號腳,背後只接著一顆 10kΩ 電阻,最多供 ~0.5mA。你接上去,ESP 的 LED 會亮(騙過你),但 WiFi 一啟動需要電流,立刻 brown-out 重開。
我就是差點被那根「漂亮的 5V」騙走。記住一句:會抖的才是電源。
踩雷②:TX/RX 分不出來?用猜的
兩根訊號腳靜態都是被 pull-up 的 5V,電表根本分不出誰是 TX、誰是 RX。最省事的做法不是硬分,而是:先猜一個方向接,燒上去看 log;如果一直 Response timeout,就把 YAML 裡的 tx_pin 和 rx_pin 對調、OTA 重燒即可——不用拆線,而且 TX/RX 接反不會燒壞,只是收不到資料。
⚠️ 安全提醒:接上冷氣之後,絕對不要再把 ESP 的 USB 接到電腦。冷氣 5V 的地線是參考 220V 火線的,萬一開關電源絕緣擊穿,地線會直接帶 220V,USB 線就成了 220V 直通電腦主機板的路徑——輕則炸主板、重則觸電。所有韌體更新一律走 OTA。
韌體:ESPHome 的 midea 元件
ESPHome 設定我拆成「共用檔 + 各台入口檔」:共用檔放所有設定,每台冷氣各有一個小入口檔,用 substitutions 帶入名字、再 !include 共用檔。這樣兩台(客廳、臥室)共用同一份設定。
入口檔長這樣:
# aircon-living.yaml — 客廳那台
substitutions:
device_name: maxe-aircon-living
friendly_name: 客廳冷氣
packages:
common: !include maxe-aircon-common.yaml
共用檔的關鍵片段:
esp32:
board: esp32-c3-devkitm-1
variant: ESP32C3 # ESP32-C3 顯式寫這行,連線比較穩
# Logger 走 USB Serial/JTAG,把 UART0 留給冷氣
logger:
hardware_uart: USB_SERIAL_JTAG
uart:
tx_pin: GPIO21
rx_pin: GPIO20
baud_rate: 9600
climate:
- platform: midea
id: ac
name: ${friendly_name}
period: 1s
beeper: true # 收到指令時冷氣會嗶一聲(後面會聊這個)
supported_modes:
- COOL
- DRY
- FAN_ONLY # 送風,防霉吹乾要用
# ...(HEAT / HEAT_COOL 視機種)
# 接到冷氣後一律走 OTA,不要再插 USB
ota:
- platform: esphome
password: !secret ota_password
幾個重點:
logger: hardware_uart: USB_SERIAL_JTAG:ESP32-C3 的 log 改走內建的 USB Serial/JTAG,把 UART0(GPIO20/21)整條留給冷氣,不會互搶。beeper: true:每次冷氣收到指令會嗶一聲,debug 階段很有用(聽得到指令有沒有送到)。這個後來變成一個有趣的問題(見最後一段)。- HomeKit 的恆溫器只有 關/冷/暖/自動,沒有「送風」「除濕」。我另外加了兩個 template switch 補位,這樣在 Apple 家庭 App 裡也能單獨開送風/除濕。
收訊小提醒:ESP32-C3 內建的 PCB 天線如果收訊弱、容易斷線,最簡單的解法是換一塊有外接天線座的板子——我後來改用 XIAO ESP32-C3、把附的天線接上去就很穩了。
Home Assistant 跑在哪:N100 + Proxmox + HAOS
我的 Home Assistant 不是裝在樹莓派,而是跑在一台 N100 迷你電腦上:先裝 Proxmox VE(PVE),再在上面開一個 HAOS(Home Assistant OS) 虛擬機。
為什麼用 HAOS 而不是 Container/Core 版?因為裝 add-on 最方便——HACS、各種官方/社群 add-on 點一點就裝好,不用自己搞 Docker。N100 夠力又省電,順便還能在 PVE 上跑別的服務。
在 PVE 上安裝 HAOS 我參考這支影片:How to install Home Assistant OS on Proxmox。
冷氣的 ESP 燒好、上線後,Home Assistant 會自動發現它,climate 實體就出現了。接下來才是真正好玩的部分。
接進 HA 之後,真正好玩的是自動化
硬體只是把冷氣「接上網」,價值其實在自動化。以下都做在 Home Assistant 的 package(YAML)裡,跟 ESPHome 無關。entity_id 用 placeholder(climate.living 之類)示意。
自動防霉吹乾
冷氣關機後蒸發器是濕的,悶著容易發霉。所以我做了:只要冷氣從 cool/dry 被關掉(包含用遙控器關),先自動轉送風吹乾 N 分鐘,再真正關機。
- alias: 客廳冷氣 自動防霉-關機前吹乾
trigger:
- platform: state
entity_id: climate.living
from: [cool, dry]
to: "off"
condition:
- condition: state
entity_id: input_boolean.living_antimold_enabled
state: "on"
action:
- service: climate.set_hvac_mode
target: { entity_id: climate.living }
data: { hvac_mode: fan_only }
- service: timer.start
target: { entity_id: timer.living_fan_dry }
data:
duration: "{{ (states('input_number.fan_dry_minutes')|float*60)|int }}"
吹乾倒數結束,再有一條把它真正關掉。
睡眠定時
設定 N 小時 → 到時關機。關機這一下會被上面的「自動防霉」接手吹乾,所以睡前定時關=定時關 + 自動吹乾,一條龍。
踩雷③:殘留的 timer 會偷偷關機
這類「倒數 + 自動關」最容易出的 bug:吹乾/睡眠倒數還在跑時,我手動把冷氣切回冷氣模式(接手了),結果舊的倒數時間到,照樣把冷氣關掉——你會覺得「我明明在吹冷氣,怎麼自己關了?」。
解法是補上幾條「中止」自動化:偵測到你手動接手(climate 狀態變了)或手動關機,就把殘留的倒數 timer 取消掉;而且倒數結束、要關機前,再確認一次冷氣狀態真的還在該關的狀態才動手。邊界顧好,這套自動化才不會反過來惹人厭。
壓軸:怎麼讓「自動化靜音、自己按還是嗶」
還記得 beeper: true 嗎?冷氣每收到一個指令會嗶一聲。白天自己操作時,這聲嗶是很好的回饋;但半夜睡眠定時自動關機、自動吹乾也會嗶——這就很吵。
我要的效果是:自動化發的指令靜音,自己(或遙控器)手動按的照嗶。
ESPHome 的 midea 元件可以在執行期切換 beeper(midea_ac.beeper_on / beeper_off),我把它做成一個 switch。最直覺的版本(v1)是 dashboard 放一個總開關,睡前手動關、早上開回——最低成本,但要手動。
進階版(本篇重點)是「依來源靜音」:每一條會送指令的自動化,在送指令前先把 beeper 關掉,事後再還原。 聽起來簡單,但這裡藏了一個 race condition。
那個 race condition
睡眠定時到時關機(sleep_finished)這一下,會連鎖觸發自動防霉(antimold)去轉送風。也就是兩條自動化幾乎同時在動。如果每條都「關 beeper → 送指令 → 開回 beeper」,那麼 sleep_finished 開回 beeper 的瞬間,剛好撞上 antimold 要送的「轉送風」指令——於是那聲嗶就漏了出來。
解法:debounce 還原 + 尊重手動關
我把「還原 beeper」從每條自動化裡抽出來,改成一條獨立的還原自動化:
當「睡眠倒數」和「吹乾倒數」兩個 timer 都閒置滿 10 秒,才把 beeper 開回去。
這 10 秒的 debounce 剛好吸收掉 sleep_finished → antimold 之間那段不到一秒的空檔,整條連鎖跑完、塵埃落定後才還原,就不會漏嗶。
還有一個我想要的行為:如果是我自己手動把 beeper 關著的,自動化跑完不應該擅自幫我開回來。 為此加了一個隱藏旗標 input_boolean.living_beeper_restore_pending:
- 靜音時:只有當 beeper 原本是開的,才標記旗標、再關掉。
- 還原時:只有旗標有立才還原。
所以你自己關的 beeper(沒立旗標)永遠不會被自動化打開。
靜音片段(放在每條送指令的自動化前面):
- if:
- condition: state
entity_id: switch.living_beeper
state: "on"
then:
- service: input_boolean.turn_on
target: { entity_id: input_boolean.living_beeper_restore_pending }
- service: switch.turn_off
target: { entity_id: switch.living_beeper }
- delay: "00:00:01" # 等 ESP 套用 flag,指令封包才會帶「不嗶」
還原自動化:
- alias: 客廳冷氣 beeper 還原(自動指令後)
trigger:
- platform: state
entity_id: timer.living_sleep
to: "idle"
for: "00:00:10"
- platform: state
entity_id: timer.living_fan_dry
to: "idle"
for: "00:00:10"
condition:
- condition: state
entity_id: input_boolean.living_beeper_restore_pending
state: "on"
- condition: state
entity_id: timer.living_sleep
state: idle
- condition: state
entity_id: timer.living_fan_dry
state: idle
action:
- service: switch.turn_on
target: { entity_id: switch.living_beeper }
- service: input_boolean.turn_off
target: { entity_id: input_boolean.living_beeper_restore_pending }
最後的行為:
- 你沒動(beeper 開著)+ 自動化關機/吹乾 → 全程靜音,週期結束後自動還原成開。
- 你自己把 beeper 關著 → 自動化照樣靜音,週期結束維持關,不會被偷開。
- 你/遙控器手動操作 → 照嗶。

心得
- 低成本、全本地不靠雲端,而且 Home Assistant 的自動化全都能用——CP 值很高。
- 硬體最大的雷是電源判斷(會抖的才是 +5V)和 pinout(一定要自己量,別信慣例)。
- 真正的價值在自動化;而自動化的魔鬼在邊界——殘留的 timer、連鎖觸發的 race condition,這些不處理,自動化反而會變成困擾。
- 完整程式碼(ESPHome + Home Assistant package)開源在 GitHub:kevinypfan/esp32-aircontroller。
如果你家也有 Midea OEM 的冷氣,歡迎照著玩玩看,也歡迎分享你的接法與改良!