Craftiarenko
Published © CC BY-SA

Soil moisture sensor w-parasite with ESPHome

Soil moisture sensor based on ESP32-WROOM-32D using w-parasite PCB and ESPHome firmware

AdvancedFull instructions provided2 hours4,104
Soil moisture sensor w-parasite with ESPHome

Things used in this project

Hardware components

Espressif ESP-WROOM-32D
×1
JLCPCB w-parasite PCB
×1
Resistor SMT 0805 (Check PCB BOM)
×8
Capacitor SMT 0805 (Check PCB BOM)
×6
XB3303A
×1
HT7333
×1
LL4148
×1
MMBT3904
×2
Battery holder 18650
×1
Battery 18650
×1

Software apps and online services

Home Assistant
Home Assistant

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Custom parts and enclosures

ESP01 case

STL files for the ESP01 case

w-parasite case

STL files for the w-parasite case

Schematics

w-parasite schematic

Code

ESPHome firmware

YAML
My ESPHome configuration for w-parasite
substitutions:
  sleep_duration: 30min
  expire_after: 40min
  after_boot_time: 30s
  wifi_timeout: 30s
  connection_timeout: 5s
  threshold_voltage: "3.5"
  maximal_voltage: "4.2"
  minimal_voltage: "2.75"
  dropdown_voltage: "0.09"

esphome:
  name: w-parasite
  friendly_name: w-parasite
  includes:
    - brownout-off.h
  on_boot:
    - priority: 600
      then:
        - if:
            condition:
              lambda: |-
                if(esp_sleep_get_wakeup_cause() == 4) return true;
                else return false;
            then:
              - wait_until:
                  condition:
                    wifi.connected:
                  timeout: $wifi_timeout
              - if:
                  condition:
                    not:
                      wifi.connected:
                  then:
                    - button.press: go_sleep

    - priority: -100
      then:
        - switch.turn_on: pwm_enable
        
        - wait_until:
            condition:
              or:
                - api.connected:
                - mqtt.connected:
            timeout: $connection_timeout

        - if:
            condition: # Check whether it is power-on or deep sleep exit
              lambda: |-
                if(esp_sleep_get_wakeup_cause() != 4) return true;
                else return false;
            then:
              - wait_until:
                  condition:
                    api.connected:
                  timeout: $connection_timeout 
              - delay: $after_boot_time

        - switch.turn_on: init_complete

        - repeat:
            count: 5
            then:
              - component.update: vsens_raw
              - component.update: vbat_raw
              - delay: 100ms
        - button.press: go_sleep

esp32:
  board: lolin_d32
  framework:
    type: arduino

# Enable logging
logger:
  # level: NONE
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "your encryption key"

mqtt:
  id: mqtt_client
  port: !secret mqtt_port
  broker: !secret mqtt_broker
  username: !secret mqtt_user
  password: !secret mqtt_password
  discovery_prefix: homeassistant
  topic_prefix: w-parasite
  discovery: true
  birth_message:
    topic:
    payload: online
  will_message:
    topic:
    payload: offline

ota:
  safe_mode: false # Block the safe mode counter (otherwise it will be written to the flash at each cycle)
  password: "your ota password"

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

  # use_address: w-parasite.local

  manual_ip:
    static_ip: 192.168.0.9
    gateway: 192.168.0.1
    subnet: 255.255.255.0

  fast_connect: on

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "W-Parasite Fallback Hotspot"
    password: "your hotspot password"

captive_portal:

deep_sleep:
  id: deep_sleep_obj

custom_component: # Block the low voltage detector, otherwise there may be problems when turning on WiFi
- lambda: |-
    auto no_brownout = new NoBrownout();
    return {no_brownout};

sensor:
  - platform: adc
    attenuation: auto
    pin: A0
    id: vsens_raw
    name: "VSens Raw"
    update_interval: never
    unit_of_measurement: "V"
    icon: "mdi:current-dc"
    device_class: "voltage"
    state_class: "measurement"
    accuracy_decimals: 2
    filters:
      - lambda: |-
          if(id(vbat_raw).state < ${threshold_voltage}) {
            return x + (${threshold_voltage} - id(vbat_raw).state - ${dropdown_voltage});
          } else return x;
      - median:
          window_size: 7
          send_every: 5


    expire_after: $expire_after
    on_value:
      - mqtt.publish_json:
          topic: "voltage"
          payload: |-
              root["VSens Raw"] = id(vsens_raw).state;

  - platform: copy
    source_id: vsens_raw
    name: "Moisture Sensor"
    id: vsens
    force_update: true 
    unit_of_measurement: "%"
    icon: "mdi:water-percent"
    device_class: "moisture"
    state_class: "measurement"
    filters:
      - lambda: "return min(100.0, max(0.0, (double)(x - id(vsens_air).state) / (id(vsens_water).state - id(vsens_air).state) * 100));"

    expire_after: $expire_after
    on_value:
      - mqtt.publish_json:
          topic: "moisture"
          payload: |-
              root["Moisture Sensor"] = id(vsens).state;

  - platform: adc
    attenuation: auto
    pin: A3
    id: vbat_raw
    name: "VBat Raw"
    update_interval: never
    unit_of_measurement: "V"
    icon: "mdi:current-dc"
    device_class: "voltage"
    state_class: "measurement"
    accuracy_decimals: 2
    filters:
      - multiply: 2
      - median:
          window_size: 7
          send_every: 5

    expire_after: $expire_after
    on_value:
      - mqtt.publish_json:
          topic: "voltage"
          payload: |-
              root["VBat Raw"] = id(vbat_raw).state;

  - platform: copy
    source_id: vbat_raw
    name: "Battery Sensor"
    id: vbat
    force_update: true 
    unit_of_measurement: "%"
    icon: "mdi:battery"
    device_class: "battery"
    state_class: "measurement"
    filters:
      - lambda: return min(100.0, max(0.0, (double)(x - ${minimal_voltage}) / (${maximal_voltage} - ${minimal_voltage}) * 100));

    expire_after: $expire_after
    on_value:
      - mqtt.publish_json:
          topic: "battery"
          payload: |-
              root["Battery Sensor"] = id(vbat).state;

output:
  - platform: gpio
    pin: GPIO17
    id: discharge

  - platform: ledc
    pin: GPIO16
    frequency: 500000
    channel: 0
    id: pwm

button:
  - platform: template
    id: go_sleep
    on_press:
      - if:
          condition:
            switch.is_off: adj_enable
          then:
            - switch.turn_off: pwm_enable
            - deep_sleep.enter:
                id: deep_sleep_obj
                sleep_duration: $sleep_duration

switch:
  - platform: template
    id: pwm_enable
    optimistic: true
    turn_on_action:
      - output.turn_on: discharge
      - output.set_level:
          id: pwm
          level: "50%"

    turn_off_action:
      - output.set_level:
          id: pwm
          level: "0%"
      - output.turn_off: discharge

  - platform: template
    id: init_complete
    optimistic: true
    restore_mode: ALWAYS_OFF

  - platform: template
    id: adj_enable
    name: "Adjustment Switch"
    optimistic: true
    restore_mode: ALWAYS_OFF
    
    turn_on_action:
      switch.turn_on: pwm_enable

    turn_off_action:
      - delay: 1s
      - if:
          condition:
            switch.is_on: init_complete
          then:
            - button.press: go_sleep

number:
  - platform: template
    name: "VSens Water"
    id: vsens_water
    restore_value: true
    initial_value: 0
    optimistic: true
    min_value: 0
    max_value: 5
    step: 0.01

  - platform: template
    name: "VSens Air"
    id: vsens_air
    restore_value: true
    initial_value: 1
    optimistic: true
    min_value: 0
    max_value: 5
    step: 0.01

interval:
  - interval: 0.1s
    then:
      if:
        condition:
          - switch.is_on: adj_enable
        then:
          - component.update: vsens_raw
          - component.update: vbat_raw

brownout-off.h

C Header File
Just in case, even with the installed capacitor, you can't flash the board over the air
#include "esphome.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

class NoBrownout : public Component {
 public:
  void setup() override {
    // This will be called once to set up the component
    // think of it as the setup() call in Arduino
    WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  }
  void loop() override {
  }
};

w-parasite

repository with files for PCB and original firmware

Credits

Craftiarenko
6 projects • 13 followers
Making simple things difficult :)
Contact

Comments

Please log in or sign up to comment.