|
|
||
|---|---|---|
| Plantmonitor.ino | ||
| Plantmonitor.jpg | ||
| Plantmonitor_assembled.jpg | ||
| Plantmonitor_box.jpg | ||
| Plantmonitor_breadboard_cables.jpg | ||
| Plantmonitor_breadboard_cables_v2.jpg | ||
| Plantmonitor_closeup_box.jpg | ||
| Plantmonitor_components.jpg | ||
| Plantmonitor_schematic.jpg | ||
| Plantmonitor_schematic_v2.jpg | ||
| README.de.md | ||
| README.md | ||
| dashboard_one_sensor.png | ||
| dashboard_two_sensors.png | ||
| devtools_service_dfplayer.png | ||
| devtools_service_dfplayer_de.png | ||
| esphome_device_type.png | ||
| pflanzenmonitor.yaml | ||
| pflanzenmonitor_base.yaml | ||
README.md
Talking Plantmonitor
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:
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
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:
And here with all components and inside the box:
Box modification
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:
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:
And here the updated Breadboard cables:
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":
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:
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_intervalwith symbolmdi:timerpflanzenmonitor_minimum_moisture_percentwith symbolmdi: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:
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:












