|
|
ESPHome 的賣點是「不用寫程式」。
但有些事情 YAML 做不到,這時候要用 lambda。
好消息:九成的情況只需要一行。

lambda 是什麼
一小段 C++ 程式碼,嵌在 YAML 裡。
就這樣。
什麼時候需要它
- 數值換算(單位轉換、公式計算)
- 條件判斷(YAML 的 if 不夠用的時候)
- 組合多個感測器的值
- 自訂顯示文字
- 存取別的元件的狀態
什麼時候不需要
- 單純的濾波 → 用 filters
- 單純的開關 → 用 switch
- 簡單的自動化 → 用 on_xxx 事件
寫在哪裡

一、濾波器裡
- sensor:
- - platform: aht10
- temperature:
- name: "溫度"
- filters:
- - lambda: return x - 1.5; # 補償 ESP32 發熱
複製代碼
x 就是進來的值,return 出去的就是結果。
丟掉這一筆:
- - lambda: |-
- if (x < -40 || x > 80) return {}; // 明顯錯誤的讀值
- return x;
複製代碼
return {} 代表「不要往下傳」。
二、範本感測器(template sensor)
做一個「算出來的」感測器:
- sensor:
- # 兩個原始感測器
- - platform: aht10
- temperature:
- id: temp_in
- name: "室內溫度"
- humidity:
- id: humid_in
- name: "室內濕度"
- # 算出來的:露點
- - platform: template
- name: "露點"
- unit_of_measurement: "°C"
- device_class: temperature
- lambda: |-
- float t = id(temp_in).state;
- float rh = id(humid_in).state;
- if (isnan(t) || isnan(rh)) return {};
- float a = 17.27, b = 237.7;
- float alpha = (a * t) / (b + t) + log(rh / 100.0);
- return (b * alpha) / (a - alpha);
- update_interval: 60s
複製代碼
重點:
- id(元件id).state 取得別的元件的值
- isnan() 檢查是不是「還沒有值」 —— 這個不能省,
開機時感測器還沒讀到值的話會是 NaN
- return {} 表示「這次不回報」
三、二元感測器
- binary_sensor:
- - platform: template
- name: "太濕了"
- device_class: moisture
- lambda: |-
- if (isnan(id(humid_in).state)) return {};
- return id(humid_in).state > 75;
複製代碼
回傳 true / false。
加遲滯(避免在門檻附近跳動):
- lambda: |-
- static bool state = false;
- float h = id(humid_in).state;
- if (isnan(h)) return {};
- if (h > 75) state = true;
- if (h < 68) state = false;
- return state;
複製代碼
static 讓變數在多次呼叫之間保留值 ——
這是做遲滯的關鍵。
四、動作裡(on_xxx)
- binary_sensor:
- - platform: gpio
- pin: GPIO13
- name: "按鈕"
- on_press:
- - lambda: |-
- if (id(temp_in).state > 28) {
- id(relay_fan).turn_on();
- } else {
- id(relay_fan).turn_off();
- }
複製代碼
五、顯示器上
- display:
- - platform: ssd1306_i2c
- model: "SSD1306 128x64"
- lambda: |-
- it.printf(0, 0, id(font_big), "%.1f°C", id(temp_in).state);
- it.printf(0, 24, id(font_small), "濕度 %.0f%%", id(humid_in).state);
複製代碼

最常用的十個寫法(可以直接抄)
1. 取得另一個元件的值
2. 檢查有沒有值
- if (isnan(id(temp_in).state)) return {};
複製代碼
3. 四捨五入到一位小數
- return round(x * 10) / 10.0;
複製代碼
4. 限制範圍
- if (x < 0) return 0;
- if (x > 100) return 100;
- return x;
複製代碼
5. 單位換算
- return x * 9.0 / 5.0 + 32.0; // 攝氏 → 華氏
- return x / 1000.0; // 毫 → 基本單位
複製代碼
6. 開關另一個元件
- id(relay1).turn_on();
- id(relay1).turn_off();
- id(relay1).toggle();
複製代碼
7. 設定燈的亮度
- auto call = id(my_light).turn_on();
- call.set_brightness(0.5); // 0.0 ~ 1.0
- call.perform();
複製代碼
8. 印日誌(除錯必備)
- ESP_LOGD("custom", "溫度是 %.2f", id(temp_in).state);
複製代碼
日誌等級:ESP_LOGE(錯誤)、ESP_LOGW(警告)、
ESP_LOGI(資訊)、ESP_LOGD(除錯)、ESP_LOGV(詳細)。
9. 回傳文字(text_sensor)
- text_sensor:
- - platform: template
- name: "空氣品質"
- lambda: |-
- float p = id(pm25).state;
- if (isnan(p)) return {"未知"};
- if (p < 15) return {"良好"};
- if (p < 35) return {"普通"};
- if (p < 55) return {"不良"};
- return {"很糟"};
- update_interval: 60s
複製代碼
text_sensor 的 return 要用大括號包起來:return {"文字"};
10. static 變數(記住上次的值)
- lambda: |-
- static float last = 0;
- float diff = x - last;
- last = x;
- return diff;
複製代碼
C++ 的基本語法(只需要知道這些)
- 每一行結尾要有分號 ;
- 變數要宣告型別:float t = 26.5;、int n = 3;、bool b = true;
- 註解用 //
- if / else 跟大部分語言一樣
- 字串比較不能用 ==(要用 strcmp 或 std::string)
YAML 的多行寫法
- lambda: |-
- 第一行;
- 第二行;
- return 結果;
複製代碼
|- 的意思是「保留換行,但去掉結尾的換行」。
單行的話可以直接寫:

除錯技巧
一、編譯錯誤怎麼看
ESPHome 的編譯錯誤很長很嚇人,但通常:
- 往上捲找第一個 error(後面的都是連鎖反應)
- 訊息會指出是哪個 lambda(通常有 main.cpp 的行號)
- 最常見的錯誤:忘記分號、型別不對、id 打錯
二、用日誌確認值
- lambda: |-
- ESP_LOGD("debug", "溫度=%.2f 濕度=%.2f",
- id(temp_in).state, id(humid_in).state);
- return x;
複製代碼
然後在 ESPHome 的 LOGS 視窗看輸出。
這是最有效的除錯方法。
三、格式化字串的寫法
- %.2f 浮點數,兩位小數
- %.0f 浮點數,不要小數
- %d 整數
- %s 字串
- %% 一個百分號
複製代碼
四、常見錯誤
- 'id' was not declared → id 打錯,或那個元件沒設 id
- expected ';' → 少了分號
- cannot convert → 型別不對(例如把 float 當成 string 回傳)
- 值一直是 nan → 沒檢查 isnan,或者來源感測器還沒讀到值
不會 C++ 怎麼辦
老實說:大部分的 lambda 你都可以用搜尋找到現成的。
- 搜尋「esphome lambda 你要做的事」
- ESPHome 的官方文件每個元件都有 lambda 範例
- 社群論壇和 Reddit 有大量現成的片段
我自己寫的 lambda 九成是從網路上改來的,
只要看懂「哪裡是我要改的部分」就夠了。
---
下一篇:用 ESP32 做電子紙家庭資訊看板 ——
超省電,可以掛在牆上幾個月不用充電。 |
|