Make a plant-biosignal sensor with M5Stack and ESPHome
Plants make small electrical signals, from microvolts to some millivolts. The signals change when the light comes on, when the water becomes low, or when the plant has stress. This guide shows how to make the sensor that records these signals. The sensor has an M5Stack ESP32, an ECG front-end chip and ESPHome, and the cost is approximately NZ$110. You will record the raw trace in Home Assistant and know how to read the rhythm of each day and the stress events. The sensor is not a validated meter for water or nutrients, but it shows the electrical activity of the plant in real time.
Purpose and scope
Plants make small electrical signals. When a plant changes in response to light, water, a wound or a change in nutrients, ions move across the cell membranes. This movement makes a voltage of less than one millivolt. You can read this voltage with electrodes on the stem[3]. Commercial units, for example the Vivent VITA1, do the same. They then use trained models to calculate the water condition and the stress condition from the drift of the signal[5].
To read a plant is the same electrical problem as to read a heartbeat. The voltage is small, it has noise, and it has high impedance. You must amplify it and not increase the noise. Thus the AD8232, a standard ECG front-end chip with a low cost, is correct for plants without changes[1]. Connect this chip to an ESP32 and ESPHome, and you have a plant-biosignal sensor that records data. The cost is approximately NZ$110.
Definitions
Evidence and limitations
We want each paper to be correct. Thus we examine each paper for information that is not solid. This information comes from the view of one grower, from a small number of sources, or from grower methods that have no controlled test. We identify this information, and we do not show it as solid.
Frequently there is no paper for the decision that you must make. Then we use the reports of other growers and the methods that work in the rooms that we know. This information can help you, but it is not the result of a laboratory test. Use the methods that work for your plants, for your room and for your meters. If a table does not agree with your crop, use the data from your crop and record the difference.
- Plants make biopotentials that you can measure. Noise at the electrode is an important problem in engineering.
- A DIY hobby system with an AD8232 and ESPHome to record data.
- The estimate of NPK, of the irrigation that is necessary, or of the stress condition, from only the signal in millivolts of a DIY system.
- The outputs of models of commercial products that you make again without the trained library of the product.
- Papers on the classification of emotional states for decisions about cultivation.
If you find an error, make a report. We will correct it. Make the report on GitHub, with the name of the paper and the information that is incorrect. If you have a source, include it: Make a report of an accuracy problem. The regulations, labels and licenses of your area always override each procedure in these papers. The label weak in a paper shows the points where the risk of an error is highest.
The signal chain
The sensor is a chain from the plant to the dashboard. Two electrodes measure the stem and one electrode is the reference in the soil. The AD8232 amplifies and filters the signal. The ADS1115 digitizes it. The ESP32 sends it to Home Assistant.
Bill of materials
The cost is approximately NZ$110. The M5Stack parts are Grove modules that you connect together. The two parts that are not from M5Stack are the AD8232 front-end and the ADS1115 ADC. These two parts are the usual alternatives with a low cost, because there is no biopotential amplifier in the Grove modules. The prices are approximate values for the middle of 2026.
| Part | Function | Where to get it | Approximate NZD |
|---|---|---|---|
| M5StickC PLUS2 ESP32 with screen and LiPo battery | Controller and display | M5Stack shop (end of life, EOL: M5StampS3 replaces it) | 32 |
| ENV IV Unit (SHT40 + BMP280) | Air temperature, humidity, pressure | M5Stack shop | 14 |
| DLight Unit (BH1750) | Light intensity (lux) | M5Stack shop | 12 |
| 1-to-3 HUB Unit (passive I2C splitter) | Connect three devices to one bus | M5Stack shop | 6 |
| AD8232 ECG module (SparkFun SEN-12650 or an equivalent) | Biopotential front-end [not from M5Stack] | SparkFun · equivalent on AliExpress, approximately NZ$9 | 9 |
| ADS1115 breakout (16-bit I2C ADC) | Digitize the analog output [not from M5Stack] | Adafruit product 1085 · equivalent, approximately NZ$6 | 6 |
| Electrodes: Ag/AgCl EEG cups or stainless probes with snap leads | Skin contact [not from M5Stack] | SparkFun sensor cable and electrode pads, or a medical set of Ag/AgCl electrodes | 14 |
| Electrode gel (Ten20) and micropore tape | Contact with low impedance | Pharmacy | 9 |
| DuPont jumper wires, a small prototype board, heatshrink | Connections between the AD8232 and the ADS1115 | Your spare parts, or an electronics shop | 8 |
| Total: approximately NZ$110. If the controller is an M5StampS3 and not the StickC, the total is less than NZ$95. The alternative does not have the screen that is on the board of the StickC. | |||
Wiring
After the controller, the sensor has one I2C bus. The HUB connects this bus to three devices. The only analog wire in the sensor goes from the output of the AD8232 to the A0 pin of the ADS1115. The electrodes connect to the header with three pins of the AD8232.
| From | Pin | To | Pin or target |
|---|---|---|---|
| M5 Grove Port A | SDA (G32) | HUB → all I2C devices | SDA |
| M5 Grove Port A | SCL (G33) | HUB → all I2C devices | SCL |
| M5 Grove Port A | 5V / GND | HUB → ENV and DLight. 3.3V to ADS1115 and AD8232 | VDD / GND |
| AD8232 | OUTPUT | ADS1115 | A0 |
| AD8232 | 3.3V / GND | controller 3.3V / GND | — |
| AD8232 | LA / RA / RL | electrodes | upper stem, lower stem, soil |
Electrodes and their position
Contact impedance is the resistance between the metal of the electrode and the tissue of the plant. A bad connection decreases the signal and lets noise go in before the signal goes to the amplifier. The AD8232 does all the work after this point. The quality of the electrode contact is the one variable that you control. You can use one of these two alternatives[4]:
This alternative gives the best signal quality. Fill the cup with conductive gel and attach it to the stem with tape. It does not cause a wound in the plant, but the gel becomes dry. Replace the gel after some days.
Put the probe 2 to 3 mm (0.08 to 0.12 in) below the epidermis. This alternative is more stable. It is almost the same as the method of commercial pin contacts, which measure the signal in the plant. But it causes a wound in the plant. Thus put the probe in the plant one time only, and use a clean sterilized probe.
- Put the pair of electrodes along the stem
Put LA (sense +) on the upper stem near a node. Put RA (sense −) 5 to 15 cm (2 to 6 in) below LA on the same stem. This pair of electrodes records the signal that moves along the stem.
- Put the reference in the soil
Put RL (the driven reference) in the moist soil of the root zone. It removes the common-mode hum. Make sure that you connect RL.
- Gel and tape
Put a small quantity of Ten20 below each surface contact. Then attach the contact with micropore tape, which gives a light and stable pressure. If the baseline increases slowly and has more noise, the cause is usually that the electrode becomes dry. The cause is not usually a problem in the plant.
- Wait until the signal is stable
When metal touches wet tissue, ion exchange occurs at the surface and makes a small voltage. This voltage is a half-cell potential. Wait 10 to 30 minutes for the voltage to become stable. The first drift comes from the chemistry of the electrode and not from the plant.
ESPHome firmware
The full configuration follows. Put your Wi-Fi, API and OTA secrets in the file secrets.yaml of ESPHome. Install the firmware from the ESPHome dashboard. Home Assistant then finds the sensor automatically. For a StampS3 or a Core2, change the board setting. If it is necessary, also change the two I2C pins.
esphome:
name: plant-biosignal
friendly_name: Plant Biosignal
esp32:
board: m5stick-c # Core2 -> m5stack-core2 ; Stamp S3 -> m5stack-stamps3
framework: { type: arduino }
logger:
api:
encryption: { key: !secret api_key }
ota:
- platform: esphome
password: !secret ota_pw
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# ---- I2C bus (Grove Port A on StickC PLUS2 / Core2) ----
i2c:
sda: 32
scl: 33
frequency: 400kHz
scan: true
# ---- 16-bit ADC hub ----
ads1115:
- address: 0x48
sensor:
# === PLANT BIOPOTENTIAL ===
- platform: ads1115
multiplexer: 'A0_GND' # AD8232 OUTPUT on A0, single-ended
gain: 4.096 # FSR covers the 0-3.3V swing
name: "Plant Biopotential (raw)"
id: bio_raw
unit_of_measurement: "V"
accuracy_decimals: 4
update_interval: 1s
filters:
- median: { window_size: 5, send_every: 1 } # kill spikes
# Centre on the ~1.5V bias -> signed millivolts (the number you watch)
- platform: template
name: "Plant Biopotential"
id: bio_mv
unit_of_measurement: "mV"
accuracy_decimals: 2
update_interval: 1s
lambda: |-
return (id(bio_raw).state - 1.5) * 1000.0; // trim 1.5 to your measured baseline
# Slow envelope = the "trend" a commercial unit charts every 5 min
- platform: template
name: "Plant Signal Trend"
unit_of_measurement: "mV"
accuracy_decimals: 2
lambda: |- return id(bio_mv).state;
filters:
- exponential_moving_average: { alpha: 0.02, send_every: 30 }
# === ENVIRONMENT (ENV IV Unit) ===
- platform: sht4x
address: 0x44
temperature: { name: "Grow Air Temp", id: air_t }
humidity: { name: "Grow Humidity", id: air_rh }
- platform: bmp280_i2c # older ESPHome: use "bmp280"
address: 0x76
temperature: { name: "Grow Baro Temp" }
pressure: { name: "Grow Pressure" }
# === LIGHT (DLight Unit) ===
- platform: bh1750
address: 0x23
name: "Grow Light"
update_interval: 10s
# === DERIVED: VPD (kPa) ===
- platform: template
name: "Grow VPD"
unit_of_measurement: "kPa"
accuracy_decimals: 2
update_interval: 10s
lambda: |-
float t = id(air_t).state;
float rh = id(air_rh).state;
float es = 0.6108 * exp(17.27 * t / (t + 237.3)); // saturation vapour pressure
return es * (1.0 - rh / 100.0);
# === OPTIONAL: electrode lead-off detection ===
binary_sensor:
- platform: gpio
pin: 26 # AD8232 LO+ ; pick a free GPIO for your board
name: "Electrode Lead-off"
device_class: problem
How to read plant-biosignal data
The values are changes of the potential. They are not a calibrated physiological unit. Compare a plant with the same plant at other times. Do not compare plants with each other in raw millivolts.
| Signal that you see | Possible condition |
|---|---|
| The signal increases and decreases smoothly each day, at the same time as the light sensor | The circadian rhythm of a plant in good condition. This rhythm is the baseline. |
| A fast deflection, then the signal goes back to the baseline slowly, after you touch or move the plant | A variation potential. It is the usual signal of a wound or stress. |
| A step change at the same time as the lights come on or go off | Response to light. The data in the reference show a detection rate of approximately 85% with this hardware[3] |
| The baseline increases slowly and has more noise, and there is no plant event | The electrode becomes dry. Put new gel on it. It is not a plant problem. |
| 50/60 Hz noise that is larger than all the other signals | Mains pickup. Make the leads shorter, twist them, and make the soil reference better. |
| A signal that does not change, at a rail (0 or full scale) | A lead is off or a contact has no connection. Examine the binary sensor. |
To get close to the inference of a commercial unit, record data for some weeks. Record the raw trace, the VPD, the light and the irrigation events. Then find your correlations that occur again. For example, the amplitude of the signal can decrease strongly before you see wilt, as a first sign of water stress. The commercial product includes this library of correlations. Send the trace to the plant-state dashboard to use it together with your other telemetry.
Limits, calibration and safety
References
- Marques JAL et al. (2023). From AD8232 to biopotentials sensors: open-source project and benchmark. Electronics (MDPI), 12(4):833. https://www.mdpi.com/2079-9292/12/4/833
- AD8232 bioelectric signal processing with ESP32 (2025). arXiv:2505.18173. (source not from a journal, for example from a manufacturer). https://arxiv.org/pdf/2505.18173
- Plant bioelectrical signals for environmental and emotional state classification (2024). PMC. (AD8232 at 400 Hz; ~85% lamp on/off detection accuracy.). https://www.ncbi.nlm.nih.gov/pmc/articles/PMC12649952/
- Verma A. Interacting with the Flora: an ESP32 + AD8232 + BH1750 + DS18B20 plant-signal rig. Hackster.io. (source not from a journal, for example from a manufacturer). https://www.hackster.io/ankurverma608/interacting-with-the-flora-7ef65f
- Vivent Biosignals, VITA1 plant-driven cultivation sensor (manufacturer documentation). (source not from a journal, for example from a manufacturer). https://vivent-biosignals.com/plant-driven-cultivation/
- ESPHome, ADS1115 4-channel 16-bit A/D converter component (documentation). (source not from a journal, for example from a manufacturer). https://esphome.io/components/sensor/ads1115/
The Cannabis White Papers · CC BY-NC 4.0 · version 1.2 · updated 2026-07-18