ProtoBoksi opas

Paine-eron mittaus ESPHomella

Tällä sivulla tehdään nopea käytännön vertailu kahden eri paineanturiratkaisun välillä: halpa HX711/HX710-tyyppinen moduuli ja Sensirion SDP810. Molemmilla voi saada numeroita ruudulle, mutta vain toinen on järkevä valinta pienten paine-erojen mittaamiseen.

Tärkeä huomio: nämä koodit on siivottu julkaisua varten. Älä koskaan julkaise omaa ESPHome API -salausavainta, OTA-salasanaa tai WiFi-tietoja nettisivulla, videolla tai GitHubissa. Käytä aina !secret-muuttujia tai vaihda avaimet ennen julkaisua.

1. Mitä tässä oikeasti mitataan?

Kun tässä puhutaan ilmanpaineen mittauksesta, kyse ei ole sääaseman absoluuttisesta ilmanpaineesta. Tässä mitataan paine-eroa: eli kahden pisteen välistä eroa.

Paine-eroa voidaan mitata esimerkiksi suodattimen yli, ilmanvaihtokanavasta, 3D-tulostimen kotelosta, puhaltimen aiheuttamasta imusta tai pienestä ylipaineesta. Yksikkönä käytetään yleensä pascaleita, eli Pa.

Hyvä nyrkkisääntö: jos mittaat ilmanvaihtoa, suodattimen tukkoisuutta tai pienen puhaltimen vaikutusta, mittausalueen pitää olla pieni. Kymmenien tuhansien pascalien anturi voi olla aivan väärä työkalu muutaman pascalin mittaukseen.

2. Anturien erot lyhyesti

Halpa HX711/HX710-tyyppinen moduuli

Tämä sopii hyvin kokeiluun ja periaatteen näyttämiseen. Sen avulla voidaan nähdä, että paine muuttuu, mutta lukeman muuttaminen luotettavaksi pascal-arvoksi vaatii kalibrointia.

Paras käyttötapa: “muuttuuko paine johonkin suuntaan?”

Sensirion SDP810

Sensirion SDP810 on tehty nimenomaan pienille paine-eroille. Esimerkiksi ±125 Pa versio sopii hyvin ilmanvaihdon, suodattimien ja pienten puhaltimien mittauksiin.

Paras käyttötapa: “paljonko paine-ero oikeasti on?”

Ominaisuus Halpa HX711/HX710-tyyppinen moduuli Sensirion SDP810
Hinta Erittäin halpa Selvästi kalliimpi
Mittausalue Usein suuri tai epäselvästi ilmoitettu Pieni ja tähän käyttöön sopiva, esimerkiksi ±125 Pa
Käyttöönotto Vaatii usein raakadataa ja kalibrointia I2C-väylällä siisti ja suora lukeminen
Luotettava pieni paine-ero Hankala Hyvä valinta
ProtoBoksi-suositus Kokeiluun Oikeaan mittaukseen

3. Kytkennät

Sensirion SDP810 D1 minillä

SensirionD1 mini / ESP8266
VCC3.3 V
GNDGND
SDAD1
SCLD2

Halpa moduuli ESP32:lla

ModuuliESP32
VCC3.3 V tai moduulin vaatima käyttöjännite
GNDGND
DOUT / DATAGPIO19
SCK / CLKGPIO18

Tarkista aina oman moduulin merkinnät. Halvoissa moduuleissa nimeäminen voi vaihdella.

4. Halvan HX711/HX710-tyyppisen moduulin ESPHome-koodi

Tässä esimerkissä halpa paineanturimoduuli luetaan ESPHomessa hx711-alustalla. Koodissa raakadata muutetaan prosentiksi kahden kalibrointipisteen avulla: tyhjä ja täysi.

Tämä esimerkki on tehty IBC-säiliön mittausajatuksella, mutta sama periaate sopii myös opetusvideolle: ensin luetaan raakadata, sitten muutetaan se käyttökelpoisempaan muotoon.

Huomio: tässä raw_empty ja raw_full ovat esimerkkiarvoja. Ne pitää mitata omasta anturista ja omasta rakenteesta. Muuten prosentti- ja litralukemat eivät pidä paikkaansa.
esphome:
  name: paine-halpa
  friendly_name: Paine halpa anturi

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: !secret api_encryption_key

ota:
  - platform: esphome
    password: !secret ota_password

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

  ap:
    ssid: "Paine Halpa Fallback"
    password: !secret fallback_password

captive_portal:

substitutions:
  tank_liters: "1000.0"
  tank_height_cm: "100.0"

  # Vaihda nämä oman mittauksen mukaan.
  raw_empty: "8500"
  raw_full: "26500"

i2c:
  sda: 21
  scl: 22
  scan: true

font:
  - file: "gfonts://Roboto"
    id: font_small
    size: 12

  - file: "gfonts://Roboto"
    id: font_big
    size: 26

sensor:
  # Esimerkkinä mukana myös TOF-etäisyysanturi OLED-näyttöä varten.
  - platform: vl53l0x
    name: "Etäisyys"
    id: tof_distance
    address: 0x29
    update_interval: 300ms
    long_range: true

  # Halvan paineanturimoduulin raakadata.
  - platform: hx711
    id: ibc_raw
    name: "IBC raakadata"
    dout_pin: GPIO19
    clk_pin: GPIO18
    gain: 128
    update_interval: 2s
    filters:
      - sliding_window_moving_average:
          window_size: 10
          send_every: 1

  # Raakadatan muunnos prosenteiksi.
  - platform: template
    id: ibc_percent
    name: "IBC täyttöaste"
    unit_of_measurement: "%"
    accuracy_decimals: 1
    update_interval: 2s
    lambda: |-
      float raw = id(ibc_raw).state;
      float empty = atof("${raw_empty}");
      float full = atof("${raw_full}");

      if (full == empty) return NAN;

      float percent = (raw - empty) / (full - empty) * 100.0;

      if (percent < 0.0) percent = 0.0;
      if (percent > 100.0) percent = 100.0;

      return percent;

  - platform: template
    id: ibc_liters
    name: "IBC litraa jäljellä"
    unit_of_measurement: "L"
    accuracy_decimals: 0
    update_interval: 2s
    lambda: |-
      return id(ibc_percent).state / 100.0 * atof("${tank_liters}");

  - platform: template
    id: ibc_height
    name: "IBC veden korkeus"
    unit_of_measurement: "cm"
    accuracy_decimals: 1
    update_interval: 2s
    lambda: |-
      return id(ibc_percent).state / 100.0 * atof("${tank_height_cm}");

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    update_interval: 200ms
    lambda: |-
      static uint32_t last_switch = 0;
      static bool page_ibc = false;

      if (millis() - last_switch > 4000) {
        page_ibc = !page_ibc;
        last_switch = millis();
      }

      if (!page_ibc) {
        it.printf(0, 0, id(font_small), "ProtoBoksi");

        if (id(tof_distance).has_state()) {
          float mm = id(tof_distance).state * 1000.0;

          it.printf(0, 20, id(font_big), "%.0f mm", mm);
          it.printf(0, 50, id(font_small), "%.1f cm", mm / 10.0);
        } else {
          it.printf(0, 20, id(font_big), "--- mm");
        }

      } else {
        it.printf(0, 0, id(font_small), "IBC tankki");

        if (id(ibc_liters).has_state()) {
          it.printf(0, 16, id(font_big), "%.0f L", id(ibc_liters).state);
          it.printf(0, 40, id(font_small), "Taytto: %.1f %%", id(ibc_percent).state);
          it.printf(0, 54, id(font_small), "Korkeus: %.1f cm", id(ibc_height).state);
        } else {
          it.printf(0, 20, id(font_big), "--- L");
        }
      }

Miten halpa anturi kalibroidaan?

  1. Kokoa anturi ja katso Home Assistantista IBC raakadata.
  2. Mittaa arvo, kun säiliö tai mittauskohta on tyhjä. Laita se arvo kohtaan raw_empty.
  3. Mittaa arvo, kun säiliö tai mittauskohta on täysi. Laita se arvo kohtaan raw_full.
  4. Jos prosentti toimii väärinpäin, vaihda raw_empty ja raw_full keskenään.

5. Sensirion SDP810 ESPHome-koodi

Tässä Sensirion SDP810 luetaan suoraan I2C-väylältä. Koodi käynnistää anturin jatkuvan paine-eromittauksen, lukee paineen, lämpötilan ja scale factorin sekä tarkistaa CRC:n.

Tämän etu halpaan moduuliin verrattuna on se, että lopputuloksena saadaan suoraan pascaleina järkevä paine-erolukema.

esphome:
  name: sensirion
  friendly_name: Sensirion SDP810
  libraries:
    - Wire

esp8266:
  board: d1_mini

logger:
  level: DEBUG

api:
  encryption:
    key: !secret api_encryption_key

ota:
  - platform: esphome
    password: !secret ota_password

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

  ap:
    ssid: "Sensirion Fallback Hotspot"
    password: !secret fallback_password

captive_portal:

i2c:
  sda: D1
  scl: D2
  scan: true
  frequency: 50kHz

globals:
  - id: sdp_started
    type: bool
    restore_value: no
    initial_value: "false"

sensor:
  - platform: template
    id: sdp_paine_ero
    name: "SDP paine-ero"
    unit_of_measurement: "Pa"
    accuracy_decimals: 2
    state_class: measurement
    icon: mdi:gauge

  - platform: template
    id: sdp_lampotila
    name: "SDP lampotila"
    unit_of_measurement: "°C"
    accuracy_decimals: 2
    state_class: measurement
    device_class: temperature

  - platform: template
    id: sdp_scale_factor
    name: "SDP scale factor"
    accuracy_decimals: 0
    state_class: measurement

interval:
  - interval: 2s
    then:
      - lambda: |-
          auto crc8 = [](const uint8_t *data, uint8_t len) -> uint8_t {
            uint8_t crc = 0xFF;
            for (uint8_t i = 0; i < len; i++) {
              crc ^= data[i];
              for (uint8_t b = 0; b < 8; b++) {
                if (crc & 0x80) {
                  crc = (crc << 1) ^ 0x31;
                } else {
                  crc <<= 1;
                }
              }
            }
            return crc;
          };

          const uint8_t addr = 0x25;

          // Käynnistä jatkuva paine-eromittaus vain kerran.
          if (!id(sdp_started)) {
            Wire.beginTransmission(addr);
            Wire.write(0x36);
            Wire.write(0x15);
            uint8_t err = Wire.endTransmission();
            if (err != 0) {
              ESP_LOGW("sdp", "Start command failed, Wire error=%u", err);
              return;
            }
            id(sdp_started) = true;
            ESP_LOGI("sdp", "SDP continuous measurement started");
            delay(10);
          }

          // Lue 9 tavua:
          // pressure(2)+crc, temperature(2)+crc, scale factor(2)+crc
          uint8_t need = 9;
          uint8_t got = Wire.requestFrom((int) addr, (int) need);
          if (got != need) {
            ESP_LOGW("sdp", "Expected %u bytes, got %u", need, got);
            return;
          }

          uint8_t buf[9];
          for (int i = 0; i < 9; i++) {
            if (!Wire.available()) {
              ESP_LOGW("sdp", "Wire underrun at byte %d", i);
              return;
            }
            buf[i] = Wire.read();
          }

          if (crc8(buf, 2) != buf[2]) {
            ESP_LOGW("sdp", "CRC fail pressure");
            return;
          }
          if (crc8(buf + 3, 2) != buf[5]) {
            ESP_LOGW("sdp", "CRC fail temperature");
            return;
          }
          if (crc8(buf + 6, 2) != buf[8]) {
            ESP_LOGW("sdp", "CRC fail scale");
            return;
          }

          int16_t dp_raw = (int16_t)((buf[0] << 8) | buf[1]);
          int16_t temp_raw = (int16_t)((buf[3] << 8) | buf[4]);
          uint16_t scale = (uint16_t)((buf[6] << 8) | buf[7]);

          if (scale == 0) {
            ESP_LOGW("sdp", "Scale factor was 0");
            return;
          }

          float pressure = (float) dp_raw / (float) scale;
          float temperature = (float) temp_raw / 200.0f;

          id(sdp_paine_ero).publish_state(pressure);
          id(sdp_lampotila).publish_state(temperature);
          id(sdp_scale_factor).publish_state(scale);

          ESP_LOGD("sdp", "raw=%d temp_raw=%d scale=%u -> %.3f Pa, %.2f C",
                   dp_raw, temp_raw, scale, pressure, temperature);

Home Assistant -kortti Sensirionille

Tällä kortilla lukemat saa nopeasti näkyviin Home Assistantin dashboardille.

type: entities
title: Sensirion SDP810
entities:
  - entity: sensor.sdp_paine_ero
    name: Paine-ero
  - entity: sensor.sdp_lampotila
    name: Anturin lämpötila
  - entity: sensor.sdp_scale_factor
    name: Scale factor

Graafi paine-erolle

Graafi tekee paine-erosta paljon helpommin ymmärrettävän kuin pelkkä numero.

type: custom:apexcharts-card
header:
  show: true
  title: Paine-ero
  show_states: true
  colorize_states: true
graph_span: 10min
series:
  - entity: sensor.sdp_paine_ero
    name: Paine-ero
    type: line
    stroke_width: 2
    unit: Pa

6. Mihin tätä voi käyttää?

Paine-eron mittaus muuttuu kiinnostavaksi heti, kun mittaus laitetaan kiinni oikeaan paikkaan.

7. Vianhaku

I2C-laite ei löydy

Tarkista SDA, SCL, käyttöjännite ja maa. ESPHome-logissa scan: true näyttää löytyykö laite väylältä. Sensirion SDP810 käyttää tässä esimerkissä osoitetta 0x25.

Lukema on väärinpäin

Vaihda letkut toisin päin tai käsittele arvo ohjelmassa miinusmerkillä. Paine-eroanturissa suunnalla on merkitystä.

Halvan anturin arvo vaeltaa

Se on tavallista halvoissa raakadataan perustuvissa ratkaisuissa. Käytä keskiarvosuodatusta, anna anturin lämmetä, tee nollaus ja muista, että raakadata ei ole sama asia kuin luotettava pascal-lukema.

Sensirion antaa CRC-virheitä

Kokeile lyhyempiä johtoja, matalampaa I2C-taajuutta ja varmista, että anturi saa oikean käyttöjännitteen. Tässä esimerkissä I2C-taajuus on laskettu arvoon 50kHz, koska se voi auttaa epävarmoissa johdotuksissa.

Yhteenveto: halpa moduuli on hyvä opetteluun ja raakamuutoksen näyttämiseen. Sensirion SDP810 on järkevä valinta silloin, kun pienestä paine-erosta halutaan oikeasti käyttökelpoinen lukema Home Assistantiin.