找回密碼
 立即註冊
惟家LINE群QRCODE
    查看: 2|回覆: 0

    ESPHome 的 Lambda:什麼時候需要寫 C++(不會也沒關係)

    [複製鏈接]

    !lvup!   100%

    363

    主題

    27

    回帖

    2200萬

    積分

    管理員

    積分
    22005752
    發表於 3 天前 | 顯示全部樓層 |閱讀模式
    ESPHome 的賣點是「不用寫程式」。

    但有些事情 YAML 做不到,這時候要用 lambda

    好消息:九成的情況只需要一行。



    lambda 是什麼

    一小段 C++ 程式碼,嵌在 YAML 裡。
    1. lambda: return x * 2;
    複製代碼

    就這樣。

    什麼時候需要它


    • 數值換算(單位轉換、公式計算)
    • 條件判斷(YAML 的 if 不夠用的時候)
    • 組合多個感測器的值
    • 自訂顯示文字
    • 存取別的元件的狀態


    什麼時候不需要


    • 單純的濾波 → 用 filters
    • 單純的開關 → 用 switch
    • 簡單的自動化 → 用 on_xxx 事件


    寫在哪裡



    一、濾波器裡
    1. sensor:
    2.   - platform: aht10
    3.     temperature:
    4.       name: "溫度"
    5.       filters:
    6.         - lambda: return x - 1.5;      # 補償 ESP32 發熱
    複製代碼

    x 就是進來的值return 出去的就是結果

    丟掉這一筆
    1.         - lambda: |-
    2.             if (x < -40 || x > 80) return {};   // 明顯錯誤的讀值
    3.             return x;
    複製代碼

    return {} 代表「不要往下傳」。

    二、範本感測器(template sensor)

    做一個「算出來的」感測器
    1. sensor:
    2.   # 兩個原始感測器
    3.   - platform: aht10
    4.     temperature:
    5.       id: temp_in
    6.       name: "室內溫度"
    7.     humidity:
    8.       id: humid_in
    9.       name: "室內濕度"
    10.   # 算出來的:露點
    11.   - platform: template
    12.     name: "露點"
    13.     unit_of_measurement: "°C"
    14.     device_class: temperature
    15.     lambda: |-
    16.       float t = id(temp_in).state;
    17.       float rh = id(humid_in).state;
    18.       if (isnan(t) || isnan(rh)) return {};
    19.       float a = 17.27, b = 237.7;
    20.       float alpha = (a * t) / (b + t) + log(rh / 100.0);
    21.       return (b * alpha) / (a - alpha);
    22.     update_interval: 60s
    複製代碼

    重點


    • id(元件id).state 取得別的元件的值
    • isnan() 檢查是不是「還沒有值」 —— 這個不能省,
        開機時感測器還沒讀到值的話會是 NaN
    • return {} 表示「這次不回報」


    三、二元感測器
    1. binary_sensor:
    2.   - platform: template
    3.     name: "太濕了"
    4.     device_class: moisture
    5.     lambda: |-
    6.       if (isnan(id(humid_in).state)) return {};
    7.       return id(humid_in).state > 75;
    複製代碼

    回傳 true / false。

    加遲滯(避免在門檻附近跳動)
    1.     lambda: |-
    2.       static bool state = false;
    3.       float h = id(humid_in).state;
    4.       if (isnan(h)) return {};
    5.       if (h > 75) state = true;
    6.       if (h < 68) state = false;
    7.       return state;
    複製代碼

    static 讓變數在多次呼叫之間保留值 ——
    這是做遲滯的關鍵。

    四、動作裡(on_xxx)
    1. binary_sensor:
    2.   - platform: gpio
    3.     pin: GPIO13
    4.     name: "按鈕"
    5.     on_press:
    6.       - lambda: |-
    7.           if (id(temp_in).state > 28) {
    8.             id(relay_fan).turn_on();
    9.           } else {
    10.             id(relay_fan).turn_off();
    11.           }
    複製代碼

    五、顯示器上
    1. display:
    2.   - platform: ssd1306_i2c
    3.     model: "SSD1306 128x64"
    4.     lambda: |-
    5.       it.printf(0, 0, id(font_big), "%.1f°C", id(temp_in).state);
    6.       it.printf(0, 24, id(font_small), "濕度 %.0f%%", id(humid_in).state);
    複製代碼



    最常用的十個寫法(可以直接抄)

    1. 取得另一個元件的值
    1. id(元件的id).state
    複製代碼

    2. 檢查有沒有值
    1. if (isnan(id(temp_in).state)) return {};
    複製代碼

    3. 四捨五入到一位小數
    1. return round(x * 10) / 10.0;
    複製代碼

    4. 限制範圍
    1. if (x < 0) return 0;
    2. if (x > 100) return 100;
    3. return x;
    複製代碼

    5. 單位換算
    1. return x * 9.0 / 5.0 + 32.0;        // 攝氏 → 華氏
    2. return x / 1000.0;                   // 毫 → 基本單位
    複製代碼

    6. 開關另一個元件
    1. id(relay1).turn_on();
    2. id(relay1).turn_off();
    3. id(relay1).toggle();
    複製代碼

    7. 設定燈的亮度
    1. auto call = id(my_light).turn_on();
    2. call.set_brightness(0.5);        // 0.0 ~ 1.0
    3. call.perform();
    複製代碼

    8. 印日誌(除錯必備)
    1. ESP_LOGD("custom", "溫度是 %.2f", id(temp_in).state);
    複製代碼

    日誌等級:ESP_LOGE(錯誤)、ESP_LOGW(警告)、
    ESP_LOGI(資訊)、ESP_LOGD(除錯)、ESP_LOGV(詳細)。

    9. 回傳文字(text_sensor)
    1. text_sensor:
    2.   - platform: template
    3.     name: "空氣品質"
    4.     lambda: |-
    5.       float p = id(pm25).state;
    6.       if (isnan(p)) return {"未知"};
    7.       if (p < 15) return {"良好"};
    8.       if (p < 35) return {"普通"};
    9.       if (p < 55) return {"不良"};
    10.       return {"很糟"};
    11.     update_interval: 60s
    複製代碼

    text_sensor 的 return 要用大括號包起來return {"文字"};

    10. static 變數(記住上次的值)
    1.     lambda: |-
    2.       static float last = 0;
    3.       float diff = x - last;
    4.       last = x;
    5.       return diff;
    複製代碼

    C++ 的基本語法(只需要知道這些)


    • 每一行結尾要有分號 ;
    • 變數要宣告型別float t = 26.5;int n = 3;bool b = true;
    • 註解用 //
    • if / else 跟大部分語言一樣
    • 字串比較不能用 ==(要用 strcmp 或 std::string)


    YAML 的多行寫法
    1.     lambda: |-
    2.       第一行;
    3.       第二行;
    4.       return 結果;
    複製代碼

    |- 的意思是「保留換行,但去掉結尾的換行」。

    單行的話可以直接寫
    1.     lambda: return x * 2;
    複製代碼



    除錯技巧

    一、編譯錯誤怎麼看

    ESPHome 的編譯錯誤很長很嚇人,但通常:


    • 往上捲找第一個 error(後面的都是連鎖反應)
    • 訊息會指出是哪個 lambda(通常有 main.cpp 的行號)
    • 最常見的錯誤:忘記分號、型別不對、id 打錯


    二、用日誌確認值
    1.     lambda: |-
    2.       ESP_LOGD("debug", "溫度=%.2f 濕度=%.2f",
    3.                id(temp_in).state, id(humid_in).state);
    4.       return x;
    複製代碼

    然後在 ESPHome 的 LOGS 視窗看輸出。

    這是最有效的除錯方法。

    三、格式化字串的寫法
    1. %.2f    浮點數,兩位小數
    2. %.0f    浮點數,不要小數
    3. %d      整數
    4. %s      字串
    5. %%      一個百分號
    複製代碼

    四、常見錯誤


    • 'id' was not declared → id 打錯,或那個元件沒設 id
    • expected ';' → 少了分號
    • cannot convert → 型別不對(例如把 float 當成 string 回傳)
    • 值一直是 nan → 沒檢查 isnan,或者來源感測器還沒讀到值


    不會 C++ 怎麼辦

    老實說:大部分的 lambda 你都可以用搜尋找到現成的。


    • 搜尋「esphome lambda 你要做的事」
    • ESPHome 的官方文件每個元件都有 lambda 範例
    • 社群論壇和 Reddit 有大量現成的片段


    我自己寫的 lambda 九成是從網路上改來的
    只要看懂「哪裡是我要改的部分」就夠了。

    ---

    下一篇:用 ESP32 做電子紙家庭資訊看板 ——
    超省電,可以掛在牆上幾個月不用充電。
    您需要登入後纔可以回帖 登入 | 立即註冊

    本版積分規則

    Archiver|手機版|惟家的智能論壇

    GMT+8, 2026-9-24 12:18 , Processed in 0.095098 second(s), 23 queries .

    快速回覆 返回頂部 返回列表