DIY-измеритель CO₂ на ESPHome

В этом примере мы своими руками соберём измеритель углекислого газа (CO₂) на плате ESP32 и датчике Sensirion SCD41 и подключим его к облаку Рокет Хоум напрямую по MQTT — через открытый драйвер generic. Никакого отдельного «моста», Zigbee-координатора или Home Assistant не нужно: устройство само публикует свои показания, появляется в Радаре и добавляется парой кликов. После этого уровень CO₂ виден на карточке устройства в веб-панели и в Алисе (приложение «Дом с Алисой»).

Драйвер generic — это открытый контракт «умного устройства» для самоделок и любых интеграций, которые умеют говорить по MQTT.

Что нам понадобится

  • Плата ESP32 (например, esp32dev / DevKit).

  • Датчик Sensirion SCD41 (или SCD40) — подключается по шине I²C четырьмя проводами: SDA, SCL, 3V3, GND.

  • Установленный ESPHome (дашборд или консольная команда esphome).

  • Активный аккаунт Рокет Хоум и Wi-Fi.

Совет

Если у вас другой популярный датчик — MH-Z19B — он подключается не по I²C, а по UART; в ESPHome для него используется платформа mhz19. Всё остальное в этом гайде остаётся тем же (см. врезку в шаге 3).

Как это работает в двух словах

Устройство раз в минуту публикует плоский JSON со своими показаниями в топик generic/{токен} — и делает это retained (с флагом «сохранять последнее сообщение»). Пример полезной нагрузки:

{ "co2_level": 812 }

Ключ co2_level — это стандартное имя функции «CO2» (уровень углекислого газа) из справочника Рокет Хоум (значение — число в ppm). Дополнительно устройство сообщает о своей доступности в generic/{токен}/availability значениями online / offline.

Именно retained-сообщение позволяет Радару увидеть устройство «вхолодную», без ожидания живого трафика.

Примечание

Топик указывается без префикса вашей локации — просто generic/{токен}. Префикс (mount point) брокер подставляет сам по вашему MQTT-аккаунту.

Шаг 1. Получаем данные доступа к MQTT

  1. Откройте раздел Настройки → MQTT.

  2. Нажмите «Добавить» — будет создан логин и сгенерирован пароль. Сохраните пароль сразу — он показывается один раз. (Если аккаунт уже есть, но пароль утерян — нажмите «Установить пароль».)

Параметры брокера: адрес ``mq.rocket-home.ru``, порт ``8883`` (TLS).

Шаг 2. Придумываем токен устройства

Токен — это постоянный идентификатор вашего устройства в топике generic/{токен}. Выберите короткое понятное имя, например co2-gostinaya.

Правила токена:

  • один сегмент пути — без /, а также без MQTT-подстановок + и #;

  • он должен быть постоянным между перезагрузками устройства;

  • не используйте «голый» UUID (32 hex-символа) — такой токен система трактует по-особому.

Шаг 3. Прошиваем ESP32 через ESPHome

Создайте конфигурацию ESPHome (например co2.yaml) и загрузите её на плату командой esphome run co2.yaml (или через дашборд ESPHome). Wi-Fi- и MQTT-пароли удобно хранить в secrets.yaml и подставлять через !secret.

esphome:
  name: co2-gostinaya

esp32:
  board: esp32dev
  framework:
    type: esp-idf   # даёт удобный TLS по корневому сертификату

logger:

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

# Шина I²C для датчика SCD41
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

# Датчик CO₂ Sensirion SCD41 (SCD40)
sensor:
  - platform: scd4x
    update_interval: 60s
    co2:
      name: "CO2"
      id: co2_value
      # При каждом измерении публикуем плоский JSON в топик generic (retained)
      on_value:
        - mqtt.publish_json:
            topic: generic/co2-gostinaya
            retain: true
            payload: |-
              root["co2_level"] = id(co2_value).state;

# Подключение к облачному MQTT Рокет Хоум
mqtt:
  broker: mq.rocket-home.ru
  port: 8883
  username: !secret rh_mqtt_login
  password: !secret rh_mqtt_password
  # не плодим авто-топики Home Assistant discovery
  discovery: false
  # служебные топики ESPHome уводим ВНЕ пространства generic/
  topic_prefix: esphome/co2-gostinaya
  # отвечаем на запрос состояния повторной публикацией —
  # так Алиса получает актуальное значение при опросе устройства
  on_message:
    - topic: generic/co2-gostinaya/get
      then:
        - mqtt.publish_json:
            topic: generic/co2-gostinaya
            retain: true
            payload: |-
              root["co2_level"] = id(co2_value).state;
  # доступность устройства — retained online/offline
  birth_message:
    topic: generic/co2-gostinaya/availability
    payload: online
    retain: true
  will_message:
    topic: generic/co2-gostinaya/availability
    payload: offline
    retain: true
  # корневой сертификат Let's Encrypt (ISRG Root X1)
  certificate_authority: |
    -----BEGIN CERTIFICATE-----
    MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
    TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
    cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
    WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
    ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
    MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
    h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
    0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
    A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
    T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
    B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
    B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
    KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbW
    nOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3Szyn
    Tnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbw
    rbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y
    53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
    BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjAN
    BgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V
    9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk
    6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRc
    Oj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktH
    CgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqv
    Hu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN
    8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+Z
    AJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqK
    OJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN
    9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgE
    EzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5i
    ItreGCc=
    -----END CERTIFICATE-----

Примечание

Вместо SCD41 — MH-Z19B. Замените блок i2c: на uart:tx_pin/rx_pin и baud_rate: 9600), а платформу датчика — на mhz19 (тот же co2: с id и on_value). У MH-Z19B есть автокалибровка (ABC) — при постоянной работе в жилом помещении её обычно оставляют включённой.

Совет

Мы «пришиваем» корневой сертификат Let’s Encrypt (ISRG Root X1, взять можно на letsencrypt.org/certs), а не отпечаток конкретного сертификата. Сертификат брокера обновляется примерно раз в 90 дней, поэтому вариант с «отпечатком» (ssl_fingerprint, типичный путь для ESP8266) пришлось бы править после каждого продления. Корневой сертификат стабилен годами — поэтому мы и выбрали ESP32 на esp-idf с certificate_authority.

Шаг 4. Находим и добавляем устройство в Радаре

  1. Откройте Настройки → Радар и нажмите «Сканировать».

  2. Среди координаторов появится карточка «Generic MQTT» — выберите её.

  3. Нажмите «Сканировать устройства» — в списке появится ваш токен (co2-gostinaya).

  4. Нажмите «Добавить». Откроется форма нового устройства: задайте Название (например «Датчик CO₂»), выберите Тип«Датчик климата», а в разделе «Датчики» добавьте функцию «CO2» (уровень углекислого газа). Сохраните — система запоминает драйвер и токен устройства (driver: generic и friendly_name с вашим токеном) автоматически.

Примечание

Тип «Датчик климата» важен: именно он в справочнике поддерживает свойство «CO2». Общий тип «Датчик» это свойство не разрешает — с ним устройство не сохранится.

Подробнее про эти экраны — в разделах Радар и Подключение устройства.

Шаг 5. Проверяем

На карточке устройства в панели появится текущее значение CO₂ (в ppm), которое обновляется по мере новых измерений. Если устройство отключить, индикатор доступности покажет offline.

Примечание

Бесплатный тариф. На бесплатном уровне обслуживания активны не более 3 устройств, и для остальных брокер не принимает публикации в топик состояния (доступность при этом всё равно может показывать online, а значение CO₂ не появляется, и устройство не находится в Радаре «вхолодную»). Чтобы датчик работал на бесплатном тарифе, отметьте в редакторе устройства признак «Приоритетное устройство» — или используйте платную подписку.

Примечание

Алиса. Если у вас привязан навык Рокет Хоум, датчик появится и в приложении «Дом с Алисой» — с текущим уровнем CO₂. Для этого устройство должно отвечать на запрос generic/{токен}/get повторной публикацией состояния — в нашей конфигурации это делает блок on_message из шага 3. Управляемые самоделки (retained-состояние + обработка команд из generic/{токен}/set) доступны из Алисы и голосом.

Полезные мелочи

Примечание

  • Retained обязателен. Без флага retain: true на топике состояния устройство не будет видно в Радаре «вхолодную».

  • Токен ≠ UUID. Не используйте «голый» UUID в качестве токена.

  • Только измерения. Датчик CO₂ ничего не принимает, поэтому топик .../set нам не нужен — устройство работает «только на чтение». А вот на .../get оно отвечает повторной публикацией состояния — без этого Алиса при опросе показывала бы датчик как недоступный.

  • Прогрев и калибровка. Первым показаниям SCD41 дайте несколько минут прогреться; для точности полезно иногда проветривать помещение (свежий воздух ≈ 400 ppm).

  • Частота публикаций. update_interval: 60s — разумный компромисс; слишком частые публикации не нужны.