Articles › Microcontrollers and code
Sensors for ESP32 toys: button, PIR, ultrasonic, light, soil, IMU, temperature
Common sensors for toys and DIY builds: how each talks to the board (digital, analog or I2C), 3.3 V vs 5 V, and which signals must be reduced before the ESP32.
- Sensors talk in three ways: digital (high/low), analog (a voltage read by the ADC), and a bus such as I2C or 1-Wire.
- ESP32 GPIOs take about 3.6 V at most. A 5 V module that sends a 5 V signal back, such as the HC-SR04, needs it reduced first.
- Use analog pins on ADC1 (GPIO32–39), because ADC2 cannot be used while Wi-Fi is on.
- Calibrate every analog sensor against the real thing, for example read dry and wet soil before you set thresholds.
Sensors are a toy’s eyes, ears and sense of touch. Before you pick one, ask two questions: how does it send data back (digital, analog or I2C), and at what voltage does it run and signal? Those two answers decide whether you can wire it straight in or need resistors in between. This lesson is a map of seven common sensor types.
Numbers refer to an original ESP32 (ESP32-WROOM) on Arduino-ESP32 core 3.x.
Three ways a sensor talks to the board
- Digital sends only high or low, read with
digitalRead(): buttons, PIR. - Analog sends a varying voltage that the board’s ADC turns into a number, read with
analogRead(): light-dependent resistors (LDR), soil moisture. The ESP32 returns 0–4095 (12 bits). - Bus sends multi-byte digital data over a protocol such as I2C (SDA, SCL wires) or 1-Wire (one data wire). You get values already in units, and several devices can share the same wires.
A fourth kind fits none of these: pulse timing, as on the HC-SR04 ultrasonic sensor, where the pulse width is the distance.
The map: seven sensor types for toys
| Sensor | Measures | Talks via | Power / signal | Know this |
|---|---|---|---|---|
| Push button | pressed/released | digital | no power; internal pull-up | needs debouncing |
| PIR | a person or pet moving | digital | check module spec; output 3.3 V | wait 30–60 s after power-up |
| HC-SR04 | distance 2–400 cm | pulse timing | 5 V; echo outputs 5 V | reduce the echo voltage |
| LDR | brightness | analog | wired as a voltage divider | not linear |
| Soil moisture (capacitive) | drier vs wetter (relative) | analog | 3.3–5.5 V; output 0–3.0 V | calibrate dry/wet |
| IMU (MPU-6050) | acceleration + rotation, 6 axes | I2C | breakout 3–5 V | address 0x68 / 0x69 |
| Temperature (BME280 / DS18B20) | temperature (+humidity, pressure) | I2C / 1-Wire | BME280 see its lesson; DS18B20 3.0–5.5 V | board heat skews readings |
3.3 V vs 5 V: the one that kills boards
The ESP32 runs at 3.3 V. An Espressif engineer on their forum put the input limit at VDD + 0.3 V, about 3.6 V, so the GPIOs do not take 5 V directly.
Keep two things apart:
- Sensor supply. A module can be powered from 5 V if it is designed for that.
- The signal coming back into the ESP32 pin must not exceed 3.3 V.
A capacitive soil module can run on 5 V but outputs at most 3.0 V, so it is safe. The original HC-SR04 runs on 5 V and its echo pin outputs 5 V, so it needs reducing (some newer versions run at 3.3 V; check the spec of the one you buy).
Before wiring a new sensor, ask “what is the highest voltage on the pin that comes back to my board?”, not just “what does it run on?”
Worked example: HC-SR04 from wiring to distance
Bring the echo voltage down with two resistors (a voltage divider): R1 = 1 kΩ from echo to the GPIO, R2 = 2 kΩ from the GPIO to ground.
- Vout = 5 V × R2 ÷ (R1 + R2) = 5 × 2 ÷ 3 ≈ 3.33 V, within the safe range.
- The total of 3 kΩ is above the 1 kΩ Adafruit recommends, so the echo pin is not asked for more current than it can give.
Turn the pulse into distance with the datasheet formula: distance (cm) = pulse time (µs) ÷ 58.
- Echo measures 1,160 µs → 1,160 ÷ 58 = 20 cm
- Check with the speed of sound, 340 m/s: 0.00116 s × 340 ÷ 2 = 0.197 m ≈ 20 cm (halved because sound goes out and back)
- The datasheet suggests a measurement cycle over 60 ms so the last echo does not mix into the next → at most about 16 readings per second
// ESP32, TRIG = GPIO5, ECHO = GPIO18 (through the divider)
const int TRIG = 5, ECHO = 18;
void setup() { Serial.begin(115200); pinMode(TRIG, OUTPUT); pinMode(ECHO, INPUT); }
void loop() {
digitalWrite(TRIG, HIGH); delayMicroseconds(10); digitalWrite(TRIG, LOW);
unsigned long us = pulseIn(ECHO, HIGH, 30000); // 30 ms timeout
if (us) Serial.println(us / 58.0); // cm
delay(60);
}
The target should be flat and large: the datasheet asks for at least 0.5 m² for its rated performance. Fabric, plush toys or angled objects return little sound and the readings jump.
Analog on the ESP32: pick the right pin
- The ADC returns 0–4095. At 11 dB attenuation it measures about 150–3,100 mV, not the full 0–3.3 V, and the ends are not accurate.
analogReadMilliVolts()returns calibrated millivolts; it beats convertinganalogRead()yourself.- ADC2 cannot be used while Wi-Fi is on (ESP-IDF docs). For a Wi-Fi toy, use ADC1 pins: GPIO32–39.
- GPIO34–39 are input-only with no internal pull-up or pull-down; a button there needs an external resistor.
LDR: wire it as a divider with a 10 kΩ resistor (Adafruit’s suggestion for indoors). Example: a dark room at ~10 lux, LDR about 10 kΩ → 3.3 × 10 ÷ (10 + 10) = 1.65 V. A lit room at 100 lux, LDR ~1.5 kΩ → 3.3 × 10 ÷ 11.5 ≈ 2.87 V. Every LDR differs; measure yours with a multimeter.
Soil moisture: read the sensor in air (driest) and in water (wettest), and use those two values as your limits. Do not copy numbers from another article; every probe differs.
Digital and bus: button, PIR, IMU, temperature
- Button:
pinMode(pin, INPUT_PULLUP)turns on an internal resistor of about 45 kΩ. Wire the button to ground; it reads LOW when pressed. Mechanical buttons bounce, so debounce in code. - PIR: the output goes to 3.3 V on motion (as Adafruit tested). Jumper H = stays high while motion continues; L = one pulse. Allow 30–60 seconds after power-up for it to settle.
- IMU MPU-6050: I2C address 0x68; pull AD0 high to move it to 0x69. Adafruit’s breakout takes 3–5 V with an onboard regulator. Good for toys that need to know when they are tilted or shaken.
- Temperature: the BME280 uses I2C and gives temperature, humidity and pressure (see the BME280 with ESP32 lesson). The DS18B20 uses 1-Wire at 3.0–5.5 V, needs a pull-up of about 4.7 kΩ on the data line, and a 12-bit conversion takes 750 ms.
Common mistakes
- HC-SR04 echo straight into the ESP32. It may work for a while, then the pin dies. Always use the divider.
- Using an ADC2 pin and losing readings when Wi-Fi starts. Move to GPIO32–39.
- INPUT_PULLUP on GPIO34–39. It does nothing; the pin floats and reads garbage.
- Testing a PIR right after power-up. False triggers; let it settle first.
- Leaving a resistive soil probe in the ground for a month. The probes corrode and readings drift. Power it only while reading, or switch to capacitive.
- A temperature sensor right next to the board. ESP32 heat makes it read high. Give it distance, or print a case with vents (see printing a case that fits).
Safety
Every sensor here runs at 3.3–5 V from the board or an enclosed adapter. Never connect a sensor to mains power. If a soil sensor lives outdoors, waterproof the electronics and the joints: moisture can short a circuit even at low voltage.
This is the last lesson of the path. Next, try combining a sensor with a servo from the previous lesson into a toy that reacts to people.
Sources
- HC-SR04 Ultrasonic Ranging Module datasheet — Elecfreaks (via SparkFun)
- ADC — Arduino-ESP32 docs (Espressif)
- GPIO and RTC GPIO — ESP-IDF Programming Guide (Espressif)
- HC-SR04: Connect the sensor — Adafruit Learning System
- Capacitive Soil Moisture Sensor SEN0193 — DFRobot Wiki
- MPU-6050 Pinouts — Adafruit Learning System
FAQ
Which soil moisture sensor is better?
Capacitive lasts longer: its electrodes are coated, so no current flows through the soil. Resistive probes with bare metal corrode from the DC current when left in the ground. A capacitive probe still needs its top electronics and cut PCB edges kept dry.
My ESP32 analog readings jump around. What helps?
Average several readings, use the calibrated analogReadMilliVolts() instead of analogRead(), and keep analog wires away from motor or servo wires.
Can I put several I2C sensors on the same wires?
Yes, as long as the addresses differ. The MPU-6050 sits at 0x68 and moves to 0x69 with its AD0 pin. Run an I2C scanner first to confirm every device shows up.
Circuits and code were tested on sample boards. Boards differ in pin layout, so check your board's pinout before you power anything.