ESP32 DHT11 CP2104 Soil Moisture Sensor Problem

Hello

i have the below ESP32 DHT11 CP2104 WIFI Bluetooth Temperature Humidity Soil Moisture Sensor:

Can somebody help me how to integrate it with Home Assistant?

I cannot find any code

Thanks in advance

It shouldn’t be too difficult. There is an instructables tutorial with arduino. You can use it to figure out, to which pins the sensors are connected.

I can share with you yaml file which I’m using to integrate soil moisture sensor readings into Home Assistant. It’s going to need some adjustments/removal of unnecessary code to make it work with your module. I’m using standalone sensors, 3 of them connected to single ESP32. My YAML file has also integrated calibration, which requires OLED display and rotary encoder. I’m also using a mosfet to cut power from display and sensors while in deep sleep, connected to GPIO12.

esphome:
  name: soil2
  platformio_options:
    upload_speed: 921600  
  on_boot:
    - priority: 1000
      then:
        lambda: |-
            # turn on power to display and sensors
            pinMode(12, OUTPUT);
            digitalWrite(12, LOW);
            delay(50); 
            // ESP_LOGI("on_boot", "prio1000");
esp32:
  board: lolin32_lite
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true

api:
  encryption:
    key: !secret api_encryption_key

ota:
  password: !secret ota_password


  # Enable fallback hotspot (captive portal) in case wifi connection fails
#  ap:
#    ssid: "Blink Fallback Hotspot"
#    password: !secret fallback_ap_password

 
globals:
  - id: gcal_wet
    type: float
    restore_value: true
    initial_value: "1.2"
  - id: gcal_dry
    type: float
    restore_value: true
    initial_value: "2.8"
  - id: last_menu_action
    type: int

deep_sleep:
  id: deep_sleep_1
  run_duration: 10s
  sleep_duration: 30min
#  wakeup_pin: 
#    number: GPIO25
    # inverted: true  

# output:
#   - platform: gpio
#     id: status_led
#     pin: GPIO22
time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Prague
    servers:
     - 0.pool.ntp.org
     - 1.pool.ntp.org
     - 2.pool.ntp.org

font:
  - file: "fonts/Roboto-Thin.ttf"
    id: roboto
    size: 12
i2c:
  sda: GPIO16
  scl: GPIO17
  frequency: 800kHz

        # const auto display_width = it.get_width();
        # const auto display_height = it.get_height();
        # // This will render the menu to the right half of the screen leaving the left half for other drawing purposes
        # // Arguments: it.menu(x, y, menu, width, height);
        # it.menu(display_width, 0, id(my_menu), display_width, display_height);

text_sensor:
  - platform: template
    name: "${name}_MAC_Address"
    lambda: 'return {WiFi.macAddress().c_str()};'
    icon: mdi:fingerprint
    id: soil2_mac
    update_interval: 1d 
  - platform: wifi_info
    ip_address:
      name: Adress_IP  
      id: soil2_ip

display:
  - platform: ssd1306_i2c
    id: my_oled
    model: "SSD1306 128x64"
    #auto_clear_enabled: false
    address: 0x3C
    lambda: |-
          //it.printf(0, 0, id(roboto), "Vb:%.2f", id(vbat).state);
          //it.printf(0, 14, id(roboto), "MC:%s", id(soil2_mac).state.c_str());
          //it.printf(0, 26, id(roboto), "IP:%s", id(soil2_ip).state.c_str());
          //it.printf(0, 38, id(roboto), "MC:%s", id(soil2_mac).state.c_str());
          //it.printf(0, 50, id(roboto), "IP:%s", id(soil2_ip).state.c_str());
          if (id(my_menu).is_active()) {
          const auto width = it.get_width();
          const auto height = it.get_height();          
          it.menu(0, 0, id(my_menu), width, height);
          } else {
          if (id(sntp_time).now().is_valid()) {
            it.strftime(0, 0, id(roboto), "%H:%M:%S", id(sntp_time).now());
          } else {
            it.print(0, 0, id(roboto), "sync in progress ...");
          }
          // it.printf(0, 20, id(roboto), "Vbat:%.2f", id(vbat).state);
          it.printf(0, 20, id(roboto), "%.0f %.0f %.0f Vb:%.2f", id(soil1).state, id(soil2).state, id(soil3).state, id(vbat).state);
          //it.printf(0, 20, id(roboto), "%.0f %.0f %.0f Vb:%.2f", id(soil1).state, id(soil2).state, id(soil3).state, id(vbat).state);
          //it.printf(0, 40, id(roboto), "%.2f %.2f %.2f", id(soil1).raw_state, id(soil2).raw_state, id(soil3).raw_state);
          }

#it.strftime(0, 0, id(roboto), "%Y-%m-%d", id(sntp_time).now());
switch:
  - platform: template
    id: sleep_switch
    optimistic: true

script:
  - id: menu_closer
    then:
      - while:
          condition:
            display_menu.is_active: my_menu
          then:
            - lambda: |-
                int mtime = id(uptime1).state - id(last_menu_action);
                ESP_LOGI("menu_closer", "menu_idle_time=%d", mtime);              
            - delay: 1s

graphical_display_menu:
  id: my_menu
  font: roboto
  active: false
  mode: rotary
  on_redraw:
    then:
      component.update: my_oled  


  items:
    - type: command
      text: Cal DRY
      on_value:
        then:
          lambda: |-
            id(gcal_dry)=id(soil1).raw_state;
            ESP_LOGI("menu_item", "Cal DRY: %f", id(gcal_dry));
            id(my_menu).hide();
    - type: command
      text: Cal WET
      on_value:
        then:
          lambda: |-
            id(gcal_wet)=id(soil1).raw_state;
            ESP_LOGI("menu_item", "Cal WET: %f", id(gcal_wet));
            id(my_menu).hide();
    - type: command
      text: 'Hide'
      on_value:
        then:
          - display_menu.hide: my_menu
          - lambda: |-
              ESP_LOGI("display_menu", "menu leave");
              id(last_menu_action)=id(uptime1).state;
          - script.stop: menu_closer
          - deep_sleep.allow: deep_sleep_1      

# Encoder to provide navigation
sensor:
  - platform: uptime
    update_interval: 1s
    id: uptime1
  - platform: rotary_encoder
    id: rotary_encoder1
    restore_mode: ALWAYS_ZERO 
    pin_a:
      number: GPIO23
      mode:
        input: true
        pullup: true
    pin_b: 
      number: GPIO18
      mode:
        input: true
        pullup: true
    on_anticlockwise:
      - display_menu.up: my_menu
      - lambda: 'id(last_menu_action)=id(uptime1).state;'
    on_clockwise:
      - display_menu.down: my_menu
      - lambda: 'id(last_menu_action)=id(uptime1).state;'
  - platform: adc
    pin: GPIO32
    name: "soil1"
    id: soil1
    attenuation: auto
    unit_of_measurement: '%'
    update_interval: 3s
    filters:
      lambda: |-
        if (x<=id(gcal_wet)) return 100;
        if (x>=id(gcal_dry)) return 0;
        return (id(gcal_dry)-x)*100/(id(gcal_dry)-id(gcal_wet));
  - platform: adc
    pin: GPIO35
    name: "soil2"
    id: soil2
    attenuation: auto
    unit_of_measurement: '%'
    update_interval: 10s
    filters:
      lambda: |-
        if (x<=id(gcal_wet)) return 100;
        if (x>=id(gcal_dry)) return 0;
        return (id(gcal_dry)-x)*100/(id(gcal_dry)-id(gcal_wet));
  - platform: adc
    pin: GPIO34
    name: "soil3"
    id: soil3
    attenuation: auto
    unit_of_measurement: '%'
    update_interval: 10s
    filters:
      lambda: |-
        if (x<=id(gcal_wet)) return 100;
        if (x>=id(gcal_dry)) return 0;
        return (id(gcal_dry)-x)*100/(id(gcal_dry)-id(gcal_wet));
  - platform: adc
    pin: GPIO33
    name: "vbat"
    id: vbat
    attenuation: auto
    unit_of_measurement: 'V'
    update_interval: 10s
    filters:
      lambda: |-
        return x*2;
  

# A de-bounced GPIO is used to 'click'
binary_sensor:
  - platform: gpio
    id: rotary_encoder_btn
    pin:
      number: GPIO19
      mode:
        input: true
        pullup: true
    filters:
      - invert:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_press:
      - lambda: 'id(last_menu_action)=id(uptime1).state;'
      - if:
          condition:
            display_menu.is_active: my_menu
          then:
            - display_menu.enter:  my_menu    
          else:
            - display_menu.show:  my_menu    
            - lambda: |-
                ESP_LOGI("display_menu", "menu enter");
                id(last_menu_action)=id(uptime1).state;
            - deep_sleep.prevent: deep_sleep_1      
            - script.execute: menu_closer


1 Like

Hey, since you got it running already in HA, what is your average battery / rechargeable battery lifetime?

Thinking of trying these. I suppose it is also possible to set the interval when the data is read, right?

Is all that config really necessary? I thought once connected to the ESP32 with wifi you just set the MQTT broker and receive the data

1 Like