ESP8266 Project for monitoring plant soil and alert when too dry.
Go to file
Julian Leyh 0d7344cb23
add multiple sensors using include file
2023-03-08 19:16:36 +01:00
Plantmonitor.ino remove usage of SoftwareSerial and update docs 2023-03-06 20:25:14 +01:00
Plantmonitor.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_assembled.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_box.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_breadboard_cables.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_breadboard_cables_v2.jpg remove usage of SoftwareSerial and update docs 2023-03-06 20:25:14 +01:00
Plantmonitor_closeup_box.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_components.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_schematic.jpg initial commit 2023-03-05 18:44:36 +01:00
Plantmonitor_schematic_v2.jpg remove usage of SoftwareSerial and update docs 2023-03-06 20:25:14 +01:00
README.de.md add multiple sensors using include file 2023-03-08 19:16:36 +01:00
README.md add multiple sensors using include file 2023-03-08 19:16:36 +01:00
dashboard_one_sensor.png add automation instructions 2023-03-07 14:33:12 +01:00
dashboard_two_sensors.png add multiple sensors using include file 2023-03-08 19:16:36 +01:00
devtools_service_dfplayer.png add first steps for ESPHome integration 2023-03-07 09:39:43 +01:00
devtools_service_dfplayer_de.png add first steps for ESPHome integration 2023-03-07 09:39:43 +01:00
esphome_device_type.png add first steps for ESPHome integration 2023-03-07 09:39:43 +01:00
pflanzenmonitor.yaml add automation instructions 2023-03-07 14:33:12 +01:00
pflanzenmonitor_base.yaml add multiple sensors using include file 2023-03-08 19:16:36 +01:00

README.md

Talking Plantmonitor

Plantmonitor.jpg

German version / Deutsche Version

This is my first proper project with ESP8266 microcontrollers. It was inspired by the Video "Fluchende Pflanze bauen!" ("Building a cursing plant!") by achNina: https://youtu.be/W30yRRa995E

Motivation

I recently got serious about programming microcontrollers, and bought an ESP32-S3 starter set. I had fun and wanted to use the knowledge to create something useful. The plants in our house often get watered too late and too much, which isn't healthy for them. So I thought, I could make good use of the project of achNina.

Aim of the project

Like in the mentioned video, I wanted to combine a moisture sensor with a speaker and motion detector, so that a sound is played whenever somebody is nearby and the soil is too dry.

In contrast to achNina, I wanted to use an ESP8266 controller, since I plan to integrate it into my Homeassistant through ESPhome as next step. Therefore, this project is an intermediate step to learn how to implement an idea.

Used components and costs

The components that got used for this project:

  • FREENOVE ESP8266 Development Board ESP-12S
  • DFPlayer Mini MP3 Player for Arduino
  • Micro-SD Card (32GB)
  • PIR detector module HC-SR505
  • Capacitative Soil Moisture Sensor
  • Mini Breadboard (80 x 54 mm, 400 Points)
  • Plastic casing (100 x 60 x 25 mm)
  • Two 1kΩ resistors
  • Jumper wires and cables
  • USB power supply with Micro USB cable

The costs for the components (without USB power supply and wires/cables) are about 28€. Most of the components are cheaper if you buy them in larger quantities.

Here an image of the main components:

Plantmonitor_components.jpg

Limitations

The ESP8266 only has one analog input pin, so you can only connect one soil sensor, unless you multipley it or use an additional chip, see also here: https://www.instructables.com/ESP8266-with-Multiple-Analog-Sensors. Another possibility would be to use an ESP32 instead.

Compared to an Arduino Uno (or the ATmega328P that it uses), I'd guess the power demand is higher and since I didn't implement Deep Sleep, using a battery would be possible, but most likely would be drained fast. This is just my feeling about this - to be sure, I'd have to meassure the real power demands.

Setup

Schematic

Plantmonitor_schematic.jpg

Breadboard cabling

For cabling, I used solid core wires, because it is more compact and IMHO looks nicer. A few of the wires are routed below the boards, so here a picture without the components:

Plantmonitor_breadboard_cables.jpg

And here with all components and inside the box:

Plantmonitor_assembled.jpg

Box modification

Plantmonitor_box.jpg

The box had exactly the right size for the small breadboard. I only had to remove the connection tongues. Since it fits perfectly, I didn't have to stick it to the base.

I could very easily drill holes using a wood drill.

For the external wires (for the speaker, soil sensor, and power supply) I drilled two 10mm holes next to each other and smoothed it using a grater.

10mm were perfect for the side hole for the motion sensor.

Here a picture, how the breadboard and the components fit into the box:

Plantmonitor_closeup_box.jpg

Calibration

To determine the boundary values for the moisture sensor, I loaded a simple test program onto the controller and looked at the serial output, noting the values for the sensor dry at air and inside water.

This is a very simple and inaccurate calibration method. For a better instruction, how it can be done, see here: https://makersportal.com/blog/2020/5/26/capacitive-soil-moisture-calibration-with-arduino

Programming

I followed the example quite closely. But since I don't like additional delays, I compared the runtime with the time when the sound was triggered before. Also, I calculate the percent of moisture according to the calibration values.

I guessed the parameter for minimum moisture percent and want to be able to set it externally later.

Used Libraries:

  • DFRobot DFPlayer Mini

Otherwise, I use the SoftwareSerial interface that is included with the ESP8266 for communicating with the DFPlayer, since I used the default serial interface for outputting status information. Otherwise I could have respectively connected the RX- with the TX-Pins and do without.

Arduino IDE Settings

As board I had to choose the entry "NodeMCU 1.0 (ESP-12E Module)" in the submenu ESP8266. As serial interface you have to choose the according serial interface. Mine was "/dev/ttyUSB0".

Improvements

No more SoftwareSerial

After studying the ESP8266 closely, I noticed that the controller has a second serial port, that can only send data (TX1, Pin D2).

Using this I can remove the SoftwareSerial library and take off load from the controller.

Here the modified schematic:

Plantmonitor_schematic_v2.jpg

And here the updated Breadboard cables:

Plantmonitor_breadboard_cables_v2.jpg

I also modified the source code with this commit.

ESPHome-Integration

The circuit will be used as is, the programming and configuration will be handled through ESPHome. I built a second board for this, to let the first one do its job until I understood and implemented everything like I want in ESPHome.

First, I need to add a new device in ESPHome. As device type I choose "ESP8266", everything else I left at default settings.

Before installing the firmware on the controller, I need to adjust the configuration. For this, I click the "Edit" button inside of ESPHome.

As board type I have to choose the proper microcontroller. For mine it is "nodemcuv2":

esphome_device_type.png

Sensors

Soil Moisture Sensor

For calibrating the sensor, I chose a simple definition for logging the values, and appended it to the configuration:

sensor:
  - platform: adc
    pin: A0
    name: "Soil Moisture"
    id: moisture1
    accuracy_decimals: 4
    update_interval: 1s
    unit_of_measurement: "v"
    icon: "mdi:water-percent"

Afterwards, I installed the firmware and looked at the output logs. It looked like this:

[20:55:57][D][sensor:126]: 'Soil Moisture': Sending state 0.77832 v with 4 decimals of accuracy
[20:56:06][D][sensor:126]: 'Soil Moisture': Sending state 0.77832 v with 4 decimals of accuracy

To get the calibration values, I took note of the values when the sensor is dry in the air, and when it is submerged into water. For me, they looked like this:

| Zustand | Wert | |=========|===========| | Luft | 0.78809 v | | Wasser | 0.30566 v |

Using these values, I can change the configuration to output the percentage. This is the updated config:

sensor:
  - platform: adc
    pin: A0
    name: "Soil Moisture"
    id: moisture1
    accuracy_decimals: 1
    update_interval: 1s
    unit_of_measurement: "%"
    icon: "mdi:water-percent"
    filters:
    - median:
        window_size: 20
        send_every: 10
        send_first_at: 5
    - calibrate_linear:
        - 0.788 -> 0.00
        - 0.300 -> 100.00
    - lambda: |
        if (x < 0) return 0;
        else if (x > 100) return 100;
        else return (x);        

PIR Sensor

The PIR sensor is very easy to add. I just append following part to the end of my configuration:

binary_sensor:
  - platform: gpio
    pin: 15
    name: "PIR Sensor"
    id: motion1
    device_class: motion

After uploading the new version to the controller (I can now use the "Wifi" option and don't have to connect it directly), the logs looked like this, when I moved my hand near the sensor:

[21:05:34][D][sensor:126]: 'Soil Moisture': Sending state 2.58388 % with 4 decimals of accuracy
[21:05:38][D][binary_sensor:036]: 'PIR Sensor': Sending state ON
[21:05:44][D][sensor:126]: 'Soil Moisture': Sending state 2.58388 % with 4 decimals of accuracy
[21:05:51][D][binary_sensor:036]: 'PIR Sensor': Sending state OFF
[21:05:53][D][binary_sensor:036]: 'PIR Sensor': Sending state ON
[21:05:54][D][sensor:126]: 'Soil Moisture': Sending state 2.58388 % with 4 decimals of accuracy
[21:06:02][D][binary_sensor:036]: 'PIR Sensor': Sending state OFF

DFPlayer Mini

ESPHome docs: https://esphome.io/components/dfplayer.html

I am interested in this action, which will be used later inside a trigger:

on_...:
  then:
    - dfplayer.play_next:

But first I have to configure the DFPlayer, including the UART interface:

uart:
  - tx_pin: 2
    baud_rate: 9600
    id: uart_dfplayer

dfplayer:
  uart_id: uart_dfplayer

Setting the volume is done by adding an on_boot trigger at the top of the configuration:

esphome:
  # ...
  on_boot:
    then:
      - dfplayer.set_volume: 20

For testing if it works, I add a simple service under api:

api:
  # ...
  services:
  - service: dfplayer_next
    then:
      - dfplayer.play_next:

After updating the firmware the service can be found in Homeassistant and can be triggered in the Developer Tools menu:

devtools_service_dfplayer.png

Clicking the "Call Service" button should make it play the next MP3 file on the SD-Card.

Automation in ESPHome

I want to implement the same functionality as the standalone program. The reason for using ESPHome was to be able to set configuration values inside Homeassistant and add a graphical output of the moisture values.

The logic should still be in the microcontroller and not done through Homeassistant's automation - otherwise it wouldn't work when there is no Wifi.

First, I need some variables that I can set in Homeassistant and have the microcontroller use them.

Variables in Homeassistant

To contrl the variables in Homeassistant, I need so-called "Helpers". They can be found in "Settings" -> "Devices & Services" in the rightmost tab "Helpers".

For the talking plantmonitor, I need two Helpers. They can be created by clicking on the "Create Helper" button at the bottom. As the type I chose "Number" and entered the name and symbol like this:

  • pflanzenmonitor_alert_interval with symbol mdi:timer
  • pflanzenmonitor_minimum_moisture_percent with symbol mdi:water-percent

Symbol and display mode can of course be chosen as you like.

I plan to use these variables globally for all plantmonitors. Otherwise, I would have to create both variables for each device, with unambiguous names.

Variables in ESPHome

I have to add variables in the ESPHome configuration, too, so I added them like this:

globals:
  - id: alert_interval
    type: int
    restore_value: yes
    initial_value: '10'
  - id: minimum_moisture_percent
    type: int
    restore_value: yes
    initial_value: '60'

They have to be connected to the Homeassistant Helpers, so I added following to the sensor section:

sensor:
  # ...
  - platform: homeassistant
    name: Alert Interval
    entity_id: input_number.pflanzenmonitor_alert_interval
    on_value:
      then:
        - lambda: 'id(alert_interval) = x;'
  - platform: homeassistant
    name: Minimum Moisture Percentage
    entity_id: input_number.pflanzenmonitor_minimum_moisture_percent
    on_value:
      then:
        - lambda: 'id(minimum_moisture_percent) = x;'

The entity IDs have to match the ones in Homeassistant. If in doubt, check there.

Script for Sound-Output

To play the sound inside a trigger, it would suffice to execute dfplayer.play_next. But I only want to play it every x seconds, depending on the just created variable. So I need a script that I can start inside the trigger.

This is how I created it in the configuration:

script:
  - id: play_alert
    then:
      - logger.log: "Too dry - alerting!"
      - dfplayer.play_next:
      - delay: !lambda 'return id(alert_interval) * 1000;'

Adding the trigger

Now everything is ready to run the script depending on the states. I chose to do this in the on_loop trigger. For this, I added the following at the beginning of the configuration, just like I did for the on_boot trigger for the volume of the DFPlayer:

  on_loop:
    then:
      - if:
          condition:
            and:
              - binary_sensor.is_on: motion1
              - lambda: 'return id(moisture1).state < id(minimum_moisture_percent);'
              - not:
                  script.is_running: play_alert
          then:
            - script.execute: play_alert

This means, when a motion was detected, the moisture level is too low, and the script is not running, the script is started. The script executes dfplayer.play_next and delays for x seconds. While it is running, it isn't started again. Only after the delay is finished, and all the other conditions still apply, it is run again.

Testing

I added a new page in Homeassistant for my plantmonitors, and added a group for the variables, as well as a graphical card for the sensor values:

dashboard_one_sensor.png

To test it, I could take the sensor out of the soil, or wait until it is dry enough. But the easiest way is to change the value of the variable, for example to 100%. As soon as I enter the detection range of the motion sensor, the sound is played.

Full configuration file

I added the configuration file here: pflanzenmonitor.yaml

Since I'm not finished yet, the file might be modified by a later commit.

Multiple plantmonitors with same configuration

Now I want to add more than one plantmonitors. The simplest solution would be to add another device in ESPHome and copy-paste the content of the YAML file, with modified device name and keys. But this is a bad idea, I would rather turn it into a template, so I can adjust it in one place.

Modifying the configuration for this is the next step.

Include file

I extracted the part of the configuration that I want to use for all plantmonitors, and put it into its own YAML file, using variables where necessary.

As parameters I need:

  • Name
  • Friendly Name
  • Calibration values (Air and Water)

To calibrate the moisture sensors, I define an additional, internal sensor outputting the raw volt values. These will be displayed only in the log and I can note and adjust them:

sensor:
  # ...
  - platform: adc
    pin: A0
    name: "Soil Moisture Raw"
    id: moisture1_raw
    accuracy_decimals: 4
    update_interval: 10s
    unit_of_measurement: "V"
    internal: true

I put this configuration at the path /config/esphome/includes/pflanzenmonitor_base.yaml using the File editor addon, here is the complete file: pflanzenmonitor_base.yaml.

Using the Include file

To integrate a new plantmonitor, I just click on "New Device" in ESPHome, set a name, for example "Pflanzenmonitor1", choose the device type "ESP8266" and edit the configuration.

The section esphome is renamed to substitutions and the calibration values are added:

substitutions:
  name: "pflanzenmonitor1"
  friendly_name: Pflanzenmonitor1
  air_value: '0.760'
  water_value: '0.280'

The rest stays as it was generated and at the end I include the common file:

<<: !include includes/pflanzenmonitor_base.yaml

That was it. After the first upload, the moisture values are logged every 10 seconds. To calibrate the sensor, I hold it in the air and after a while I submerge it into water. The two values in the configration are adjusted accordingly and the device is updated.

Dashboard with two plantmonitors:

dashboard_two_sensors.png