15 ESP32 Projects for Beginners (with Parts List & Prices in India)
Share
ESP32 projects for beginners: Getting started with the right board
The first step in any ESP32 project is choosing a development board that matches your budget and feature needs. For Indian makers, the Espressif ESP32‑S3‑WROOM‑1‑N8R8 module offers a good balance of performance and cost, while the ESP32‑S3‑WROOM‑1‑N16R8 provides extra flash for larger sketches. If you prefer an integrated display, the ESP32‑C3 board with a 0.42" OLED simplifies UI work. Check the datasheet of each module for voltage, pin‑out, and flash size before buying.
When ordering, remember Voltros ships across India, making it easy to get genuine parts quickly. After the board arrives, install the latest ESP‑IDF or Arduino core and verify the USB‑to‑UART bridge (often CP2102) works by opening a serial monitor.
Below is a checklist you can reuse for all 15 projects:
- ESP32 board (selected from above)
- Breadboard and jumper wires
- Power supply (5 V USB or 3.3 V regulator)
- Required sensors/modules (listed per project)
- Resistors, LEDs, and basic passive components
1. Blinking LED with Wi‑Fi status indicator
This classic starter project teaches you to flash an LED and read the Wi‑Fi connection state. Wire an LED (with a 220 Ω resistor) to GPIO 2 and ground. In the code, use WiFi.begin(ssid, password) and toggle the LED based on WiFi.status(). The wiring steps are simple:
- Connect the LED anode to GPIO 2 via the resistor.
- Connect the LED cathode to GND.
- Power the board via USB.
Sketch (no extra library needed, just enter your Wi-Fi details):
#include <WiFi.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const int LED_PIN = 2;
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
WiFi.begin(ssid, password);
}
void loop() {
if (WiFi.status() == WL_CONNECTED) {
digitalWrite(LED_PIN, HIGH); // connected: blink once a second
delay(500);
digitalWrite(LED_PIN, LOW);
delay(500);
} else {
digitalWrite(LED_PIN, LOW); // not connected yet: stay off
delay(200);
}
}Selection criteria: choose a board with a stable USB‑UART bridge (e.g., CP2102) to avoid connection drops. If you need a visual cue for connection loss, add a second LED on GPIO 4. The ESP32 adapter board article explains how to add external power if USB is insufficient.
Worked example: after uploading the sketch, the LED should stay off until the board connects to your router, then blink every second. This confirms both Wi‑Fi and GPIO control work.
2. Temperature & humidity monitor using DHT22
The DHT22 sensor provides accurate temperature and humidity data, perfect for indoor monitoring. Wire the sensor’s VCC to 3.3 V, GND to ground, and data pin to GPIO 15 with a 10 kΩ pull‑up resistor. The code uses the Adafruit_DHT library to read values and print them to the serial monitor.
- Connect VCC → 3.3 V, GND → GND.
- Data pin → GPIO 15, add 10 kΩ resistor to 3.3 V.
- Optional: add an OLED (from the ESP32‑C3 board) to display readings locally.
Sketch (in the Arduino IDE Library Manager install "DHT sensor library" by Adafruit, and "Adafruit Unified Sensor" if asked):
#include <DHT.h>
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
delay(2000); // the DHT22 needs about 2 seconds between readings
float humidity = dht.readHumidity();
float temperature = dht.readTemperature(); // degrees Celsius
if (isnan(humidity) || isnan(temperature)) {
Serial.println("DHT22 read failed, check the wiring and the pull-up resistor");
return;
}
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print(" C Humidity: ");
Serial.print(humidity);
Serial.println(" %");
}Selection criteria: ensure the board can supply at least 3.3 V stable power; the ESP32‑S3 modules have a built‑in regulator that handles this. If you plan to expand to multiple sensors, check the total current draw against the regulator’s limit (see the datasheet).
Worked example: after powering the board, open the serial monitor. You should see temperature in °C and humidity in % updating every 2 seconds. Use this data to trigger alerts or log to a cloud service.
3. Simple web server to control an LED
Creating a web interface lets you control hardware from any smartphone or laptop. Connect an LED to GPIO 5 with a 220 Ω resistor. The sketch sets up an HTTP server on port 80; visiting http://esp32_ip/ shows two buttons – ON and OFF – that toggle the LED.
- LED anode → GPIO 5 via resistor.
- LED cathode → GND.
- Ensure the board is on the same Wi‑Fi network as your client device.
Sketch (uses the WebServer library that comes with the ESP32 Arduino core):
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const int LED_PIN = 5;
WebServer server(80);
void handleRoot() {
server.send(200, "text/html",
"<h1>ESP32 LED</h1>"
"<p><a href=\"/on\"><button>ON</button></a> <a href=\"/off\"><button>OFF</button></a></p>");
}
void setLed(bool on) {
digitalWrite(LED_PIN, on ? HIGH : LOW);
server.sendHeader("Location", "/"); // go back to the buttons page
server.send(303);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Open http://");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/on", []() { setLed(true); });
server.on("/off", []() { setLed(false); });
server.begin();
}
void loop() {
server.handleClient();
}Selection criteria: choose a board with sufficient flash (the N16R8 variant is ideal) to store the HTML page. The ESP32‑C3 board’s built‑in OLED can display the IP address for easier access.
Worked example: after uploading, note the IP printed in the serial monitor, open it in a browser, and click the buttons. The LED should respond instantly, proving the board can handle basic HTTP requests.
4. ESP32‑Cam security snapshot
The ESP32‑Cam module combines a camera, Wi‑Fi, and micro‑SD slot, enabling remote image capture. Connect the module to a 5 V power source (via a dedicated adapter) and use the ESP32‑Cam article for wiring details. The firmware streams MJPEG video over Wi‑Fi; you can also trigger a snapshot via a HTTP GET request.
- Power → 5 V (ensure at least 500 mA).
- Connect the micro‑SD card for image storage.
- Wire a push‑button to GPIO 0 for manual capture.
Selection criteria: the ESP32‑Cam needs a stable 5 V supply; a simple USB power bank works but verify current capability. Use a micro‑SD card class 10 for smooth writes.
Worked example: after flashing the example sketch, open the streaming URL (http://esp32_ip/video) in a browser. Press the button or send /capture to save a picture on the SD card.
5. Bluetooth Low Energy (BLE) beacon
BLE beacons broadcast a small packet that smartphones can detect. Wire a 3.3 V LED to GPIO 18 to indicate advertising status. The sketch uses the BLEDevice library to start advertising a custom UUID.
- LED anode → GPIO 18 via 220 Ω resistor.
- LED cathode → GND.
- No external peripherals needed beyond power.
Sketch (the BLE library is included with the ESP32 Arduino core):
#include <BLEDevice.h>
#include <BLEAdvertising.h>
const int LED_PIN = 18;
void setup() {
pinMode(LED_PIN, OUTPUT);
BLEDevice::init("Voltros-Beacon"); // the name your phone will show
BLEAdvertising* advertising = BLEDevice::getAdvertising();
advertising->addServiceUUID("12345678-1234-1234-1234-1234567890ab"); // your own custom UUID
advertising->setScanResponse(true);
BLEDevice::startAdvertising();
digitalWrite(LED_PIN, HIGH); // LED on while advertising
}
void loop() {
delay(1000);
}Selection criteria: any ESP32 variant supports BLE, but the S3 series offers lower power consumption, useful for battery‑operated beacons. Check the datasheet for the recommended operating voltage range.
Worked example: after flashing, open a BLE scanner app on your phone. You should see the beacon name and UUID. The LED will stay on while advertising.
6. IoT temperature logger with MQTT
Combine the DHT22 sensor from project 2 with an MQTT broker (e.g., HiveMQ) to log data to the cloud. Wire the sensor as before, then add a Wi‑Fi connection and MQTT client code. Publish temperature and humidity to topics home/esp32/temp and home/esp32/hum.
- Sensor wiring identical to project 2.
- Include the
PubSubClientlibrary. - Configure broker address, port, and credentials.
Sketch (install "PubSubClient" by Nick O'Leary and "DHT sensor library" by Adafruit; the HiveMQ public broker is open to everyone, so use your own unique topic names):
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqttServer = "broker.hivemq.com";
const int mqttPort = 1883;
DHT dht(15, DHT22);
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
void connectMqtt() {
while (!mqtt.connected()) {
if (!mqtt.connect("voltros-esp32-demo")) {
delay(2000); // try again
}
}
}
void setup() {
Serial.begin(115200);
dht.begin();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
mqtt.setServer(mqttServer, mqttPort);
}
void loop() {
connectMqtt();
mqtt.loop();
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if (!isnan(temperature) && !isnan(humidity)) {
mqtt.publish("home/esp32/temp", String(temperature).c_str());
mqtt.publish("home/esp32/hum", String(humidity).c_str());
}
delay(5000); // one reading every 5 seconds
}Selection criteria: ensure the board has enough RAM for the MQTT client (the S3‑N8R8 is sufficient). If you plan to add more sensors, consider the N16R8 for extra flash.
Worked example: after connecting, open an MQTT client on your PC and subscribe to the topics. You should see live temperature updates every few seconds.
7. Voice‑controlled LED using Google Assistant
Integrate the ESP32 with IFTTT to control an LED via voice. Wire the LED to GPIO 21. In IFTTT, create a webhook that sends an HTTP request to http://esp32_ip/led?state=on or off. The ESP32 sketch parses the query string and toggles the LED.
- LED anode → GPIO 21 via resistor.
- LED cathode → GND.
- Set up port forwarding if you want remote access (or use Ngrok).
Sketch (the same web server idea as project 3, with a /led?state=on or off address for IFTTT):
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const int LED_PIN = 21;
WebServer server(80);
void handleLed() {
String state = server.arg("state"); // "on" or "off"
if (state == "on") {
digitalWrite(LED_PIN, HIGH);
} else if (state == "off") {
digitalWrite(LED_PIN, LOW);
} else {
server.send(400, "text/plain", "Use /led?state=on or /led?state=off");
return;
}
server.send(200, "text/plain", "LED " + state);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
server.on("/led", handleLed);
server.begin();
}
void loop() {
server.handleClient();
}Selection criteria: a board with a stable Wi‑Fi connection and enough flash for the HTTP parser (the N16R8 variant is safe). Verify your router allows inbound connections or use a cloud tunnel.
Worked example: say “Hey Google, turn on my ESP32 LED” to the Google Assistant linked with IFTTT. The LED should light up, confirming end‑to‑end integration.
8. Button-controlled relay switch
A relay lets a small ESP32 pin switch a much bigger load. Here a push button toggles a relay each time you press it. Use a 1-channel 5V opto-isolated relay module, and start with a low-voltage load such as a 12V LED strip. Never work on mains wiring unless you are qualified, and keep all mains connections inside a proper enclosure.
- Relay module VCC to the 5V (VIN) pin, GND to GND, IN to GPIO 26.
- Push button between GPIO 27 and GND (the sketch uses the internal pull-up).
- Load wires go to the relay COM and NO terminals only.
Sketch (many relay modules switch on when the input is LOW; if yours does, swap HIGH and LOW in the relay line):
const int RELAY_PIN = 26;
const int BUTTON_PIN = 27;
bool relayOn = false;
void setup() {
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
digitalWrite(RELAY_PIN, LOW);
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) { // button pressed
relayOn = !relayOn;
digitalWrite(RELAY_PIN, relayOn ? HIGH : LOW);
delay(300); // simple debounce
while (digitalRead(BUTTON_PIN) == LOW) {
delay(10); // wait until the button is released
}
}
}Worked example: press the button and listen for the relay click; the load switches on. Press again and it switches off.
9. PIR motion detector with LED alert
A PIR sensor detects the movement of warm bodies such as people and pets. This project lights the board LED whenever motion is seen and prints a message to the serial monitor. It uses the HC-SR501 PIR motion sensor module.
- PIR VCC to the 5V (VIN) pin, GND to GND, OUT to GPIO 27.
- The onboard LED on GPIO 2 is used as the alert.
- The module has two trimpots for sensitivity and hold time; start with both in the middle.
Sketch (no extra library needed):
const int PIR_PIN = 27;
const int LED_PIN = 2;
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
Serial.println("Warming up the PIR sensor...");
delay(60000); // PIR modules need about a minute to settle after power-up
Serial.println("Ready");
}
void loop() {
if (digitalRead(PIR_PIN) == HIGH) {
digitalWrite(LED_PIN, HIGH);
Serial.println("Motion detected");
} else {
digitalWrite(LED_PIN, LOW);
}
delay(200);
}Worked example: after the warm-up, wave your hand in front of the sensor. The LED turns on and the serial monitor prints "Motion detected".
10. Ultrasonic distance meter
The HC-SR04 ultrasonic distance sensor measures distance by timing an echo. It runs on 5V and its ECHO pin outputs 5V, which is too high for an ESP32 input, so reduce it with two resistors as shown below.
- VCC to the 5V (VIN) pin, GND to GND, TRIG to GPIO 5.
- ECHO to a 1 kΩ resistor, the other end of that resistor to GPIO 18, and a 2 kΩ resistor from GPIO 18 to GND (a simple voltage divider).
Sketch (no extra library needed):
const int TRIG_PIN = 5;
const int ECHO_PIN = 18;
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
void loop() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH); // 10 microsecond pulse starts a measurement
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // time out after 30 ms (about 5 m)
if (duration == 0) {
Serial.println("No echo");
} else {
float distanceCm = duration * 0.0343 / 2; // speed of sound is about 343 m/s
Serial.print("Distance: ");
Serial.print(distanceCm);
Serial.println(" cm");
}
delay(500);
}Worked example: hold a book in front of the sensor and move it closer and further away. The distance on the serial monitor follows your hand.
11. OLED text display
A small OLED display shows readings without a computer. This project shows how long the board has been running, and it is the starting point for showing sensor values. It is written for a 0.91 inch 4-pin OLED with an I2C connection, usually 128x32 pixels at address 0x3C. If yours is 128x64, change the height to 64.
- OLED VCC to 3.3V, GND to GND.
- SDA to GPIO 21 and SCL to GPIO 22 (the default I2C pins on classic ESP32 boards; other boards use different pins, so check your pinout).
Sketch (install "Adafruit SSD1306" and "Adafruit GFX Library" in the Library Manager):
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32 // use 64 for 128x64 displays
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found, check the wiring and the address");
while (true) {
delay(1000);
}
}
display.setTextColor(SSD1306_WHITE);
}
void loop() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Voltros ESP32");
display.setTextSize(2);
display.setCursor(0, 14);
display.print(millis() / 1000);
display.println(" s");
display.display();
delay(500);
}Worked example: the display shows the title and a counter that goes up every second. Replace the counter with a sensor reading from any earlier project.
12. Soil moisture monitor
An analog soil moisture sensor module tells you when a plant needs water. The value depends on the sensor and the soil, so you calibrate it once with a dry and a wet reading. Resistive probes corrode over time, so power the sensor only when you read it if you plan to leave it in the soil for long.
- Sensor VCC to 3.3V, GND to GND, AO (analog out) to GPIO 34.
- GPIO 34 is on ADC1, which keeps working while Wi-Fi is on.
Sketch (set DRY_VALUE and WET_VALUE after reading your own sensor in air and in a glass of water):
const int SENSOR_PIN = 34;
const int DRY_VALUE = 3200; // reading in dry air, change after calibrating
const int WET_VALUE = 1400; // reading in water, change after calibrating
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(SENSOR_PIN);
int percent = map(raw, DRY_VALUE, WET_VALUE, 0, 100);
percent = constrain(percent, 0, 100);
Serial.print("Raw: ");
Serial.print(raw);
Serial.print(" Moisture: ");
Serial.print(percent);
Serial.println(" %");
delay(1000);
}Worked example: note the raw value with the probe in dry air and in water, put those numbers into the sketch, then push the probe into a pot. The percentage rises after you water it.
13. Touch-sensitive LED
The original ESP32 has built-in touch inputs, so a bare wire can act as a button. This works on a classic ESP32 board such as the 38-pin ESP32 development board. The ESP32-S3 uses different touch pins and the ESP32-C3 has no touch inputs, so check the datasheet before using another variant.
- Jumper wire from GPIO 4 (touch pad T0) to a piece of foil or the bare end of the wire.
- The onboard LED on GPIO 2 toggles each time you touch it.
Sketch (no extra library needed; adjust THRESHOLD after reading the value on the serial monitor):
const int TOUCH_PIN = 4;
const int LED_PIN = 2;
const int THRESHOLD = 40; // touch readings drop below this value
bool ledOn = false;
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
int value = touchRead(TOUCH_PIN);
Serial.println(value);
if (value < THRESHOLD) {
ledOn = !ledOn;
digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
delay(400); // avoid toggling many times for one touch
}
delay(50);
}Worked example: open the serial monitor and note the value untouched and while touching the wire, then set the threshold between the two. Each touch switches the LED.
14. Servo sweep
A servo turns to an exact angle, which is useful for small arms, flaps and pointers. This sweep uses the SG90 9g micro servo. Power the servo from a separate 5V supply, not from the ESP32 3.3V pin, and join the grounds of the two supplies.
- Servo signal (orange or yellow) to GPIO 13.
- Servo power (red) to an external 5V supply, servo ground (brown) to that supply ground and to the ESP32 GND.
Sketch (install "ESP32Servo" by Kevin Harrington in the Library Manager):
#include <ESP32Servo.h>
Servo servo;
const int SERVO_PIN = 13;
void setup() {
servo.attach(SERVO_PIN);
}
void loop() {
for (int angle = 0; angle <= 180; angle += 2) {
servo.write(angle);
delay(15);
}
for (int angle = 180; angle >= 0; angle -= 2) {
servo.write(angle);
delay(15);
}
}Worked example: the servo arm sweeps smoothly from one end to the other and back. If it jitters, check that the grounds are joined and the 5V supply can deliver enough current.
15. Deep sleep for battery projects
A battery project must sleep between jobs. In deep sleep the ESP32 switches almost everything off and wakes on a timer. This sketch wakes every 30 seconds, counts its wake-ups and goes back to sleep. Put your sensor reading in place of the print line.
- No extra parts are needed; a USB cable or a battery is enough.
- Variables marked RTC_DATA_ATTR survive deep sleep; ordinary variables are reset on every wake-up.
Sketch (no extra library needed):
#define SLEEP_SECONDS 30
RTC_DATA_ATTR int wakeCount = 0; // kept in RTC memory across deep sleep
void setup() {
Serial.begin(115200);
delay(500);
wakeCount++;
Serial.print("Wake-up number ");
Serial.println(wakeCount);
// do your work here: read a sensor, send the value, then go back to sleep
esp_sleep_enable_timer_wakeup((uint64_t)SLEEP_SECONDS * 1000000ULL);
esp_deep_sleep_start();
}
void loop() {
// never reached, the board restarts from setup() after each sleep
}Worked example: open the serial monitor and watch the counter go up by one every 30 seconds. Because the board restarts each time, the serial monitor may need to be reconnected on some boards.
FAQ
Q: Do I need a separate USB‑to‑UART adapter for ESP32 boards?
A: Most ESP32 development boards, including the S3‑WROOM modules, have an onboard CP2102 or CH340 chip, so a separate adapter is unnecessary unless the board’s USB port is damaged.
Q: Can I power the ESP32 from a 9 V battery?
A: The ESP32’s regulator expects 5 V on the VIN pin. A 9 V battery can be used with a step‑down regulator; check the regulator’s input range in the datasheet.
Q: How much flash memory do I need for a web server project?
A: A basic web server with a simple HTML page fits in 1 MB. For larger assets, choose the N16R8 module with 16 MB flash.
Q: Is the ESP32‑Cam compatible with the ESP32‑S3 modules?
A: The ESP32‑Cam is a separate module with its own ESP32‑S chip. It can be used alongside other ESP32 boards but is not directly interchangeable.
Q: Where can I find the official ESP32 datasheet?
A: The datasheet is available on Espressif’s website; always refer to it for voltage limits, pin‑out, and current ratings.
Looking for the parts? Browse electronic components, modules and development boards at voltros.in.