[分享] 三洋速捷淨冷氣 WIFI 控制器 (ESPhome)

三洋速捷淨冷氣(美的貼牌機)室內機有提供冷氣控制接口,經由控制器可將冷氣接入 HA 遠端控制與同步狀態

一、通訊架構

Midea 冷氣 <–UART–> ESP開發板(ESPhome) <–WIFI–> Home Assistant

二、材料準備

  1. XH2.54 4P(公頭) 轉杜邦線 2.54mm 4P(母頭)
    有現成的轉換線可買,也能買材料與工具自行製作(圖中線長為 30 CM)

  1. DT-06 開發板 + USB-TO-TTL 燒錄器 + 杜邦線4P

DT-06 簡要規格

 晶片:ESP8285(相容 ESP8266), 晶片內建 1MB Flash
 尺寸:34mm×17mm×4mm
 供電電壓:4.5V~6.0V,TTL 電壓:3.3V(可相容 5.0V)
 引出腳位:STATE,Txd、Rxd、EN
 平均電流:80mA;WiFi 資料發送時 170mA;深度睡眠模式下 20μA
 WiFi支援AP、STA、AP+STA三種模式

  1. ESPHome 韌體

air-conditioner.yaml

substitutions:
  device_name: air-conditioner
  friendly_name: "冷氣"
  model_name: "SAE-V41HJ"
  min_outdoor_temperature: "-20"
  max_outdoor_temperature: "80"

packages:
  base: !include base.yaml
  board: !include board/doit_dt_06.yaml

esphome:
  comment: SANLUX $model_name

climate:
  - platform: midea
    id: midea_climate
    name: Climate
    period: 1s
    timeout: 2s
    num_attempts: 3
    autoconf: true
    beeper: true
    visual:
      min_temperature: 16 °C
      max_temperature: 30 °C
      temperature_step: 1 °C
    supported_modes:
      - FAN_ONLY
      - HEAT_COOL
      - COOL
      - HEAT
      - DRY
    custom_fan_modes:
      - SILENT
      - TURBO
    supported_presets:
      - ECO
      - BOOST
      - SLEEP
    supported_swing_modes:
      - VERTICAL
      - HORIZONTAL
      - BOTH
    outdoor_temperature:
      name: "Outdoor Temperature"
      filters:
        - lambda: |-
            if (!isnan(x) && x >= $min_outdoor_temperature && x <= $max_outdoor_temperature) {
              return x;
            }
            return NAN;

sensor:
  - platform: template
    name: "Target Temperature"
    device_class: "temperature"
    state_class: "measurement"
    unit_of_measurement: °C
    icon: mdi:thermometer
    lambda: |-
      if (id(midea_climate).mode != CLIMATE_MODE_OFF) {
        return id(midea_climate).target_temperature;
      }
      return NAN;

  - platform: template
    name: "Indoor Temperature"
    device_class: "temperature"
    state_class: "measurement"
    unit_of_measurement: °C
    icon: mdi:thermometer
    lambda: return id(midea_climate).current_temperature;

switch:
  - platform: template
    name: Beeper
    icon: mdi:volume-source
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    turn_on_action:
      midea_ac.beeper_on:

    turn_off_action:
      midea_ac.beeper_off:

button:
  - platform: template
    name: Swing Step
    icon: mdi:tailwind
    on_press:
      if:
        condition:
          lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
        then:
          midea_ac.swing_step:

  - platform: template
    name: Dump Config
    entity_category: diagnostic
    on_press:
      lambda: |-
        id(midea_climate).dump_config();

doit_dt_06.yaml

# Chip is ESP8285N08
# Features: WiFi, Embedded Flash
# Crystal is 26MHz
# Detected flash size: 1MB

esp8266:
  board: esp8285

light:
  - platform: status_led
    name: "Status LED"
    id: board_blue_led
    output: light_output

output:
  - platform: gpio
    id: light_output
    pin:
      number: GPIO4
      inverted: True

# The following are specific settings for air conditioner

logger:
  level: DEBUG
  baud_rate: 0 # Disable output to avoid conflict with RX, TX

uart:
  rx_pin: GPIO3
  tx_pin: GPIO1
  baud_rate: 9600

base.yaml

esphome:
  name: $device_name
  friendly_name: $friendly_name

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true

  ap:
    ssid: "$friendly_name"
    password: "12345678"

captive_portal:

ota:
  - platform: esphome

api:

web_server:
  version: 3
  port: 80

time:
  - platform: homeassistant

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 60s

  - platform: uptime
    name: Uptime
    type: timestamp

text_sensor:
  - platform: version
    name: "ESPHome Version"

  - platform: wifi_info
    ip_address:
      name: WiFi IP
    ssid:
      name: WiFi SSID

button:
  - platform: restart
    name: "Restart"
    id: restart_btn

  - platform: factory_reset
    id: factory_reset_btn
    name: "Factory Reset"

secrets.yaml

# Your Wi-Fi SSID and password
wifi_ssid: "WIFI_SSID"
wifi_password: "WIFI_PASSWORD"

三、控制器製作與加入 Home Assistant

  1. 連接 DT-06 開發板 + USB-TO-TTL 燒錄器 + 電腦

DT-06 <> 燒錄器
VCC <> 5V
GND <> GND
TXD <> RXD
RXD  <> TXD
  1. 查詢 電腦 裝置管理員查詢燒錄器的通訊埠編號(COMx)

  1. DT-06 開發板切換下載模式,按住 G 鍵(Boot) 不放,按一下 R 鍵(Reset) 放開,再將 G 鍵放開

  2. 使用 ESPHome CLI 進行韌體燒錄

esphome run air-conditioner.yaml --device COMx
  1. DT-06 開發板切換一般模式,按一下 R 鍵(Reset) 放開

  2. Home Assistant 加入冷氣控制器(預設名稱:冷氣)

  3. DT-06 開發板移除連接線

四、冷氣安裝控制器

  1. 打開室內機機殼,轉開接收顯示基板外殼固定螺絲,將外殼住左移拆下

  2. 連接冷氣與控制器
    (!!! +5V, GND 不可接錯,接錯設備會毀損 !!!)

冷氣 <> DT-06
GND <> GND
RX  <> TX
TX  <> RX
+5v <> VCC

  1. 在接收顯示基板外殼內找個地方將控制器用雙面膠固定

  2. 遠端測試冷氣控制

  3. 機板外殼、室內機機殼復歸

  4. 完成

備註

  1. DT-06 的 flash 只有 1MB,有的 ESPHome 版本所編譯出的 OTA image 檔案,上傳會超出可用容量,若已加入 HA 可考慮將 web_server 與 captive_portal 移除

  2. Midea 通訊協定的控制功能有限,部份冷氣控制功能(顯示螢幕開關、隨身感…)仍需透過紅外線指令擴充,可參考下一章節的紅外加強版

  3. Midea Air Conditioner 還有些選項可啟用 power_usage, humidity_setpoint 但冷氣不一定配備相關元件


紅外加強版

Midea 通訊協定的控制功能有限,部份冷氣控制功能(顯示螢幕開關、隨身感…)仍需透過紅外線指令擴充,下面增加紅外線發射器與光敏電阻的加強版控制器(ESP32C3開發板 + 5v-3.3v 邏輯電平轉換模組)

這款冷氣螢幕夜間的燈光過亮,但由於沒有直接控制螢幕開啟或關閉的指令可用,因此加入光敏電阻來偵測螢幕亮度狀態,再依亮度狀態來用紅外指令控制螢幕燈光開關

硬體部份做法是在顯示器 WIFI 圖示上方鑽孔引光(直徑約 1.4 mm),光敏電阻(5mm)正對小孔測光,再包覆兩層黑色布膠帶,用在固定與排除其它光源干擾

PS. 螢幕叠上白紙也能降低亮度

air-conditioner-plus.yaml

substitutions:
  device_name: air-conditioner-plus
  friendly_name: "冷氣"
  model_name: "SAE-V41HJ"
  min_outdoor_temperature: "-20"
  max_outdoor_temperature: "80"
  follow_me_temperature_sensor: "sensor.temperature_humidity_sensor_67c2_temperature"
  min_photoresistor_threshold: "0"
  max_photoresistor_threshold: "1000000"
  default_display_photoresistor_threshold: "14000"

packages:
  base: !include base.yaml
  board: !include board/xiao_esp32c3.yaml

esphome:
  comment: SANLUX $model_name

remote_transmitter:
  pin:
    number: GPIO7
  carrier_duty_percent: 50%

climate:
  - platform: midea
    id: midea_climate
    name: Climate
    period: 1s
    timeout: 2s
    num_attempts: 3
    autoconf: true
    beeper: true
    visual:
      min_temperature: 16 °C
      max_temperature: 30 °C
      temperature_step: 1 °C
    supported_modes:
      - FAN_ONLY
      - HEAT_COOL
      - COOL
      - HEAT
      - DRY
    custom_fan_modes:
      - SILENT
      - TURBO
    supported_presets:
      - ECO
      - BOOST
      - SLEEP
    supported_swing_modes:
      - VERTICAL
      - HORIZONTAL
      - BOTH
    outdoor_temperature:
      name: "Outdoor Temperature"
      filters:
        - lambda: |-
            if (!isnan(x) && x >= $min_outdoor_temperature && x <= $max_outdoor_temperature) {
              return x;
            }
            return NAN;

switch:
  - platform: template
    name: Beeper
    icon: mdi:volume-source
    optimistic: true
    restore_mode: RESTORE_DEFAULT_ON
    turn_on_action:
      midea_ac.beeper_on:

    turn_off_action:
      midea_ac.beeper_off:

  - platform: template
    name: "Display Switch"
    id: display_switch
    turn_on_action:
      if:
        condition:
          and:
            - lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
            - switch.is_off: display_switch
        then:
          midea_ac.display_toggle:

    turn_off_action:
      if:
        condition:
          and:
            - lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
            - switch.is_on: display_switch
        then:
          midea_ac.display_toggle:

  - platform: template
    name: "Follow Me"
    id: follow_me
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF

    # Supports cool, fan only, dry modes
  - platform: template
    name: "Prevent Direct Blowing"
    id: prevent_direct_blowing
    optimistic: true
    assumed_state: true
    turn_on_action:
      if:
        condition:
          lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
        then:
          - remote_transmitter.transmit_pronto:
              data: "0000 0073 0000 0064 009e 009f 0012 003b 0012 0013 0012 003b 0012 003a 0013 003a 0013 0013 0012 0013 0012 003b 0012 0013 0013 003a 0012 0013 0013 0013 0012 0013 0012 003b 0012 003a 0013 0013 0012 003b 0012 003a 0013 003a 0012 003b 0012 0013 0013 003a 0012 0013 0013 003a 0012 0013 0013 0013 0012 0013 0013 0012 0013 003a 0013 0013 0012 003a 0013 0013 0012 0013 0013 003a 0012 0013 0013 003a 0012 003b 0012 0013 0012 003b 0012 003a 0013 003a 0013 0013 0012 003a 0013 0013 0012 0013 0013 003a 0012 0013 0013 0013 0012 00bb 009f 009f 0012 003a 0013 0013 0012 003a 0013 003a 0013 003a 0012 0013 0013 0013 0012 003a 0013 0013 0012 003a 0013 0013 0012 0013 0013 0013 0012 003a 0013 003a 0013 0012 0013 003a 0013 003a 0012 003b 0012 003a 0013 0013 0012 003a 0013 0013 0012 003b 0012 0013 0012 0013 0013 0013 0012 0013 0013 003a 0012 0013 0013 003a 0012 0013 0013 0013 0012 003a 0013 0013 0012 003b 0012 003a 0013 0013 0012 003a 0013 003a 0013 003a 0012 0013 0013 003a 0012 0013 0013 0013 0012 003a 0013 0013 0012 0013 0013 0e4a"

    turn_off_action:
      if:
        condition:
          lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
        then:
          - remote_transmitter.transmit_pronto:
              data: "0000 0073 0000 0064 00a1 009b 0015 0037 0014 0011 0015 0037 0015 0037 0014 0039 0014 0011 0014 0011 0015 0037 0015 0011 0014 0037 0015 0011 0014 0011 0014 0011 0015 0037 0014 0039 0014 0011 0015 0037 0014 0039 0014 0037 0015 0037 0014 0012 0013 0039 0014 0012 0014 0037 0014 0012 0013 0012 0014 0012 0014 0011 0014 0039 0013 0012 0014 0039 0013 0012 0014 0011 0014 0039 0015 0011 0014 0037 0015 0037 0014 0039 0013 0012 0014 0012 0014 0037 0015 0011 0013 0039 0015 0011 0013 0012 0015 0011 0014 0037 0015 0037 0013 00bb 00a0 009c 0014 0039 0014 0011 0014 0039 0014 0037 0015 0037 0014 0012 0014 0011 0014 0039 0014 0011 0014 0039 0014 0011 0014 0012 0013 0012 0013 003a 0013 0039 0014 0012 0013 003a 0013 0039 0014 0039 0013 003a 0014 0011 0014 0039 0013 0012 0014 0039 0013 0012 0014 0012 0013 0012 0014 0011 0014 0039 0014 0012 0013 0039 0014 0012 0013 0012 0013 003a 0013 0012 0014 0039 0013 003a 0013 0039 0014 0012 0013 0012 0014 0039 0013 0012 0014 0039 0013 0012 0014 0012 0013 0012 0014 0039 0013 003a 0013 0e4a"

sensor:
  - platform: template
    name: "Target Temperature"
    device_class: "temperature"
    state_class: "measurement"
    unit_of_measurement: °C
    icon: mdi:thermometer
    lambda: |-
      if (id(midea_climate).mode != CLIMATE_MODE_OFF) {
        return id(midea_climate).target_temperature;
      }
      return NAN;

  - platform: template
    name: "Indoor Temperature"
    device_class: "temperature"
    state_class: "measurement"
    unit_of_measurement: °C
    icon: mdi:thermometer
    lambda: return id(midea_climate).current_temperature;

  - platform: resistance
    sensor: source_sensor
    configuration: UPSTREAM
    resistor: 10kOhm
    id: photoresistor_sensor
    name: Display Photoresistor
    disabled_by_default: true
    entity_category: diagnostic
    filters:
      - clamp:
          min_value: $min_photoresistor_threshold
          max_value: $max_photoresistor_threshold
    on_value:
      - lambda: |-
          if(id(midea_climate).mode == CLIMATE_MODE_OFF){
            id(display_switch).publish_state(false);
          }else if(id(display_photoresistor_threshold).has_state() && x < id(display_photoresistor_threshold).state){
            id(display_switch).publish_state(true);
          }else{
            id(display_switch).publish_state(false);
          }

  - platform: adc
    pin: GPIO2
    id: source_sensor
    attenuation: auto
    update_interval: 1s

  - platform: homeassistant
    id: follow_me_temp
    entity_id: $follow_me_temperature_sensor
    filters:
      - throttle: 10s
      - heartbeat: 2min
      - debounce: 1s
    on_value:
      if:
        condition:
          and:
            - lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
            - switch.is_on: follow_me
        then:
          midea_ac.follow_me:
            temperature: !lambda "return x;"
            beeper: false

number:
  - platform: template
    name: "Display Photoresistor Threshold"
    id: display_photoresistor_threshold
    icon: "mdi:alarm-light-outline"
    unit_of_measurement: "ohm"
    optimistic: true
    min_value: $min_photoresistor_threshold
    max_value: $max_photoresistor_threshold
    initial_value: $default_display_photoresistor_threshold
    restore_value: true
    step: 1
    entity_category: config

button:
  - platform: template
    name: Swing Step
    icon: mdi:tailwind
    on_press:
      if:
        condition:
          lambda: return id(midea_climate).mode != CLIMATE_MODE_OFF;
        then:
          midea_ac.swing_step:

  - platform: template
    name: Active Clean
    on_press:
      - remote_transmitter.transmit_pronto:
          data: "0000 0073 0000 0064 00a0 009c 0015 0037 0014 0011 0014 0039 0014 0039 0014 0037 0014 0012 0014 0011 0014 0039 0014 0011 0014 0039 0014 0011 0014 0013 0013 0011 0014 0039 0014 0039 0014 0011 0014 0039 0014 0037 0014 0039 0014 0039 0014 0011 0014 0039 0014 0012 0013 0039 0014 0012 0014 0011 0014 0011 0014 0012 0014 0037 0014 0012 0014 0039 0013 0012 0014 0011 0014 0039 0014 0011 0014 0012 0014 0037 0014 0039 0014 0039 0014 0039 0014 0039 0013 0012 0014 0039 0014 0037 0014 0012 0014 0011 0014 0012 0013 0012 0014 00ba 00a0 009c 0015 0037 0013 0012 0014 0039 0014 0037 0014 0039 0014 0012 0013 0012 0014 0039 0014 0011 0014 0039 0014 0011 0014 0012 0013 0013 0013 0039 0014 0037 0014 0012 0014 0039 0013 0039 0014 0039 0014 0039 0014 0011 0014 0039 0014 0011 0014 0039 0014 0011 0014 0012 0014 0011 0014 0012 0013 0039 0014 0012 0014 0037 0014 0012 0014 0012 0013 0039 0014 0011 0014 0012 0013 0039 0014 0039 0014 0039 0014 0039 0013 0039 0014 0012 0014 0037 0014 0039 0014 0012 0013 0012 0014 0011 0014 0012 0014 0e4a"

  - platform: template
    name: Dump Config
    entity_category: diagnostic
    on_press:
      lambda: |-
        id(midea_climate).dump_config();

xiao_esp32c3.yaml

# Chip is ESP32-C3 (QFN32) (revision v0.4)
# Features: WiFi, BLE, Embedded Flash 4MB (XMC)
# Crystal is 40MHz

esp32:
  board: seeed_xiao_esp32c3

sensor:
  - platform: internal_temperature
    name: "Internal Temperature"

# The following are specific settings for air conditioner

uart:
  rx_pin: GPIO20
  tx_pin: GPIO21
  baud_rate: 9600

參考資訊

1個讚

窩操~ 太強大了