Voice-Controlled Home Assistant Using ESP32
Create your own smart home assistant with wake word detection, appliance control, and real-time sensor monitoring — all on an ESP32.
Project Overview
This project turns your ESP32 microcontroller into a fully functional voice-controlled home assistant. Using serial commands (simulated voice input), you can control lights, a fan, AC, and a door — while monitoring real-time temperature and humidity with a DHT22 sensor displayed on an I2C LCD.
The system features an RGB LED status indicator, a buzzer for audio feedback, and a clean serial interface. It's perfect for learning home automation concepts and can be extended with a real microphone module (INMP441) and Edge Impulse / TensorFlow Lite for true on-device voice recognition.
Key Features
Components Required
Pin Connections & Wiring
Connect all components to the ESP32 as shown in the table below. All LEDs should be connected through 220Ω resistors.
| Component | ESP32 Pin | Wire Color | Notes |
|---|---|---|---|
| LCD SDA | D21 | Green | I2C Data |
| LCD SCL | D22 | Yellow | I2C Clock |
| LCD VCC / GND | 3V3 / GND | Red / Black | |
| DHT22 SDA | D15 | Orange | Data pin |
| DHT22 VCC / GND | 3V3 / GND | Red / Black | |
| Light LED | D13 | White | Via 220Ω |
| Fan LED | D12 | Blue | Via 220Ω |
| AC LED | D14 | Cyan | Via 220Ω |
| Door LED | D27 | Yellow | Via 220Ω |
| RGB Red | D25 | Red | Via 220Ω |
| RGB Green | D26 | Green | Via 220Ω |
| RGB Blue | D33 | Blue | Via 220Ω |
| Buzzer + | D32 | Magenta | Buzzer – to GND |
How It Works
The system works by reading text commands from the Serial Monitor (simulating speech-to-text output). Here's the complete processing pipeline:
The loop() function handles three things concurrently using non-blocking timers: reading the DHT22 sensor every 2 seconds, processing incoming serial characters into a command string, and updating the LCD every 3 seconds.
When a command is received, the RGB LED turns yellow during processing, then returns to green when complete. Successful commands trigger a double-beep; unrecognized commands trigger a triple-beep error tone.
Full Arduino Code
Copy the complete code below and paste it into your Arduino IDE or Wokwi project as voice_home_assistant.ino.
/* * VOICE-CONTROLLED HOME ASSISTANT - ESP32 * * Features: * - Voice command recognition via serial (simulated) * - Control multiple home devices (lights, fan, AC, door) * - Temperature and humidity monitoring (DHT22) * - LCD display for status * - RGB LED for visual feedback * - Buzzer for audio feedback * * Voice Commands: * - "turn on light" / "turn off light" * - "turn on fan" / "turn off fan" * - "turn on ac" / "turn off ac" * - "open door" / "close door" * - "show temperature" * - "show humidity" * - "turn on all" / "turn off all" * - "status" */ #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <DHT.h> // ── Pin Definitions ───────────────────────────── #define DHT_PIN 15 #define DHT_TYPE DHT22 #define LIGHT_PIN 13 #define FAN_PIN 12 #define AC_PIN 14 #define DOOR_SERVO_PIN 27 #define RGB_RED 25 #define RGB_GREEN 26 #define RGB_BLUE 33 #define BUZZER_PIN 32 #define MIC_PIN 34 // Analog pin for mic simulation // ── I2C LCD & DHT ─────────────────────────────── LiquidCrystal_I2C lcd(0x27, 16, 2); DHT dht(DHT_PIN, DHT_TYPE); // ── Device States ─────────────────────────────── bool lightState = false; bool fanState = false; bool acState = false; bool doorState = false; // false=closed, true=open float temperature = 0; float humidity = 0; // ── Voice Command Buffer ───────────────────────── String voiceCommand = ""; // ── Function Prototypes ────────────────────────── void processVoiceCommand(String command); void controlLight(bool state); void controlFan(bool state); void controlAC(bool state); void controlDoor(bool state); void readSensors(); void updateLCD(); void setRGB(int r, int g, int b); void playConfirmationBeep(); void playErrorBeep(); void printStatus(); // ── setup() ───────────────────────────────────── void setup() { Serial.begin(115200); // Initialize LCD lcd.init(); lcd.backlight(); lcd.setCursor(0, 0); lcd.print("Voice Home"); lcd.setCursor(0, 1); lcd.print("Assistant"); dht.begin(); // Pin modes pinMode(LIGHT_PIN, OUTPUT); pinMode(FAN_PIN, OUTPUT); pinMode(AC_PIN, OUTPUT); pinMode(DOOR_SERVO_PIN, OUTPUT); pinMode(RGB_RED, OUTPUT); pinMode(RGB_GREEN, OUTPUT); pinMode(RGB_BLUE, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); pinMode(MIC_PIN, INPUT); // Initial LOW states digitalWrite(LIGHT_PIN, LOW); digitalWrite(FAN_PIN, LOW); digitalWrite(AC_PIN, LOW); digitalWrite(DOOR_SERVO_PIN, LOW); setRGB(0, 0, 255); // Blue — starting up playConfirmationBeep(); delay(2000); Serial.println("\n╔═══════════════════════════════════════╗"); Serial.println("║ VOICE-CONTROLLED HOME ASSISTANT ║"); Serial.println("╚═══════════════════════════════════════╝\n"); Serial.println("System Ready! Listening for commands..."); Serial.println("\nAvailable Commands:"); Serial.println(" - turn on/off light"); Serial.println(" - turn on/off fan"); Serial.println(" - turn on/off ac"); Serial.println(" - open/close door"); Serial.println(" - show temperature"); Serial.println(" - show humidity"); Serial.println(" - turn on/off all"); Serial.println(" - status\n"); lcd.clear(); lcd.print("Ready!"); setRGB(0, 255, 0); // Green — ready } // ── loop() ────────────────────────────────────── void loop() { // Read DHT sensor every 2 seconds static unsigned long lastSensorRead = 0; if (millis() - lastSensorRead > 2000) { readSensors(); lastSensorRead = millis(); } // Build command from serial characters if (Serial.available() > 0) { char c = Serial.read(); if (c == '\n' || c == '\r') { if (voiceCommand.length() > 0) { voiceCommand.toLowerCase(); voiceCommand.trim(); Serial.println("\n🎤 Command: " + voiceCommand); setRGB(255, 255, 0); // Yellow — processing processVoiceCommand(voiceCommand); voiceCommand = ""; delay(500); setRGB(0, 255, 0); // Green — done } } else { voiceCommand += c; } } // Refresh LCD every 3 seconds static unsigned long lastLCDUpdate = 0; if (millis() - lastLCDUpdate > 3000) { updateLCD(); lastLCDUpdate = millis(); } delay(50); } // ── Command Processor ─────────────────────────── void processVoiceCommand(String command) { bool recognized = true; if (command.indexOf("turn on light") >= 0 || command.indexOf("light on") >= 0) { controlLight(true); lcd.clear(); lcd.print("Light: ON"); } else if (command.indexOf("turn off light") >= 0 || command.indexOf("light off") >= 0) { controlLight(false); lcd.clear(); lcd.print("Light: OFF"); } else if (command.indexOf("turn on fan") >= 0 || command.indexOf("fan on") >= 0) { controlFan(true); lcd.clear(); lcd.print("Fan: ON"); } else if (command.indexOf("turn off fan") >= 0 || command.indexOf("fan off") >= 0) { controlFan(false); lcd.clear(); lcd.print("Fan: OFF"); } else if (command.indexOf("turn on ac") >= 0 || command.indexOf("ac on") >= 0 || command.indexOf("turn on air") >= 0) { controlAC(true); lcd.clear(); lcd.print("AC: ON"); } else if (command.indexOf("turn off ac") >= 0 || command.indexOf("ac off") >= 0 || command.indexOf("turn off air") >= 0) { controlAC(false); lcd.clear(); lcd.print("AC: OFF"); } else if (command.indexOf("open door") >= 0 || command.indexOf("door open") >= 0) { controlDoor(true); lcd.clear(); lcd.print("Door: OPEN"); } else if (command.indexOf("close door") >= 0 || command.indexOf("door close") >= 0) { controlDoor(false); lcd.clear(); lcd.print("Door: CLOSED"); } else if (command.indexOf("temperature") >= 0 || command.indexOf("temp") >= 0) { lcd.clear(); lcd.print("Temp: "); lcd.print(temperature); lcd.print("C"); Serial.println("Temperature: " + String(temperature) + "°C"); playConfirmationBeep(); } else if (command.indexOf("humidity") >= 0) { lcd.clear(); lcd.print("Humidity: "); lcd.print(humidity); lcd.print("%"); Serial.println("Humidity: " + String(humidity) + "%"); playConfirmationBeep(); } else if (command.indexOf("turn on all") >= 0 || command.indexOf("all on") >= 0) { controlLight(true); controlFan(true); controlAC(true); lcd.clear(); lcd.print("All Devices: ON"); } else if (command.indexOf("turn off all") >= 0 || command.indexOf("all off") >= 0) { controlLight(false); controlFan(false); controlAC(false); lcd.clear(); lcd.print("All Devices: OFF"); } else if (command.indexOf("status") >= 0) { printStatus(); } else { recognized = false; Serial.println("❌ Command not recognized!"); lcd.clear(); lcd.print("Unknown Cmd"); playErrorBeep(); } if (recognized && command.indexOf("temperature") < 0 && command.indexOf("humidity") < 0 && command.indexOf("status") < 0) { playConfirmationBeep(); Serial.println("✅ Command executed!"); } delay(2000); } // ── Device Control Helpers ────────────────────── void controlLight(bool state) { lightState = state; digitalWrite(LIGHT_PIN, state ? HIGH : LOW); Serial.println("💡 Light: " + String(state ? "ON" : "OFF")); } void controlFan(bool state) { fanState = state; digitalWrite(FAN_PIN, state ? HIGH : LOW); Serial.println("🌀 Fan: " + String(state ? "ON" : "OFF")); } void controlAC(bool state) { acState = state; digitalWrite(AC_PIN, state ? HIGH : LOW); Serial.println("❄️ AC: " + String(state ? "ON" : "OFF")); } void controlDoor(bool state) { doorState = state; if (state) { for (int i = 0; i <= 90; i += 5) { analogWrite(DOOR_SERVO_PIN, map(i,0,180,0,255)); delay(15); } } else { for (int i = 90; i >= 0; i -= 5) { analogWrite(DOOR_SERVO_PIN, map(i,0,180,0,255)); delay(15); } } Serial.println("🚪 Door: " + String(state ? "OPEN" : "CLOSED")); } // ── Sensor & Display ──────────────────────────── void readSensors() { humidity = dht.readHumidity(); temperature = dht.readTemperature(); if (isnan(humidity) || isnan(temperature)) { temperature = 25.0 + random(-5,5); humidity = 60.0 + random(-10,10); } } void updateLCD() { lcd.clear(); lcd.setCursor(0,0); lcd.print("T:"); lcd.print(temperature,1); lcd.print("C H:"); lcd.print(humidity,0); lcd.print("%"); lcd.setCursor(0,1); lcd.print("L:"); lcd.print(lightState?"1":"0"); lcd.print(" F:"); lcd.print(fanState?"1":"0"); lcd.print(" A:"); lcd.print(acState?"1":"0"); lcd.print(" D:"); lcd.print(doorState?"O":"C"); } // ── RGB & Buzzer ──────────────────────────────── void setRGB(int r, int g, int b) { analogWrite(RGB_RED, r); analogWrite(RGB_GREEN, g); analogWrite(RGB_BLUE, b); } void playConfirmationBeep() { tone(BUZZER_PIN, 1000, 100); delay(150); tone(BUZZER_PIN, 1500, 100); } void playErrorBeep() { for (int i=0; i<3; i++) { tone(BUZZER_PIN,500,100); delay(150); } } // ── Status Print ──────────────────────────────── void printStatus() { Serial.println("\n╔════════════ SYSTEM STATUS ════════════╗"); Serial.println("║ Light: " + String(lightState?"ON ":"OFF") + " ║"); Serial.println("║ Fan: " + String(fanState ?"ON ":"OFF") + " ║"); Serial.println("║ AC: " + String(acState ?"ON ":"OFF") + " ║"); Serial.println("║ Door: " + String(doorState ?"OPEN ":"CLOSED") + " ║"); Serial.println("║ Temperature: " + String(temperature) + "°C ║"); Serial.println("║ Humidity: " + String(humidity) + "% ║"); Serial.println("╚═══════════════════════════════════════╝\n"); lcd.clear(); lcd.print("Status Shown"); playConfirmationBeep(); }
Wokwi diagram.json
Save this as diagram.json in your Wokwi project. It defines all components and their connections automatically.
{
"version": 1,
"author": "Voice Home Assistant",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
{
"type": "wokwi-lcd1602", "id": "lcd1",
"top": -100, "left": 200,
"attrs": { "pins": "i2c" }
},
{ "type": "wokwi-dht22", "id": "dht1", "top": 240.3, "left": 369, "attrs": {} },
{ "type": "wokwi-led", "id": "led1",
"top": -50, "left": 500,
"attrs": { "color": "white", "label": "Light" } },
{ "type": "wokwi-led", "id": "led2",
"top": 10, "left": 500,
"attrs": { "color": "blue", "label": "Fan" } },
{ "type": "wokwi-led", "id": "led3",
"top": 70, "left": 500,
"attrs": { "color": "cyan", "label": "AC" } },
{ "type": "wokwi-led", "id": "led4",
"top": 130, "left": 500,
"attrs": { "color": "yellow", "label": "Door" } },
{ "type": "wokwi-led", "id": "rgb_red",
"top": -80, "left": 600,
"attrs": { "color": "red", "label": "RGB-R" } },
{ "type": "wokwi-led", "id": "rgb_green",
"top": -20, "left": 600,
"attrs": { "color": "green", "label": "RGB-G" } },
{ "type": "wokwi-led", "id": "rgb_blue",
"top": 40, "left": 600,
"attrs": { "color": "blue", "label": "RGB-B" } },
{ "type": "wokwi-buzzer", "id": "bz1", "top": 120, "left": 600, "attrs": {} },
{ "type": "wokwi-resistor", "id": "r1", "top": -50, "left": 550, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r2", "top": 10, "left": 550, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r3", "top": 70, "left": 550, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r4", "top": 130, "left": 550, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r5", "top": -80, "left": 650, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r6", "top": -20, "left": 650, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "r7", "top": 40, "left": 650, "attrs": { "value": "220" } }
],
"connections": [
[ "esp:TX0", "$serialMonitor:RX", "", [] ],
[ "esp:RX0", "$serialMonitor:TX", "", [] ],
[ "esp:GND.1", "lcd1:GND", "black", [] ],
[ "esp:3V3", "lcd1:VCC", "red", [] ],
[ "esp:D21", "lcd1:SDA", "green", [] ],
[ "esp:D22", "lcd1:SCL", "yellow", [] ],
[ "esp:D15", "dht1:SDA", "orange", [] ],
[ "esp:3V3", "dht1:VCC", "red", [] ],
[ "esp:GND.1", "dht1:GND", "black", [] ],
[ "esp:D13", "r1:1", "white", [] ],
[ "r1:2", "led1:A", "white", [] ],
[ "led1:C", "esp:GND.2", "black", [] ],
[ "esp:D12", "r2:1", "blue", [] ],
[ "r2:2", "led2:A", "blue", [] ],
[ "led2:C", "esp:GND.2", "black", [] ],
[ "esp:D14", "r3:1", "cyan", [] ],
[ "r3:2", "led3:A", "cyan", [] ],
[ "led3:C", "esp:GND.2", "black", [] ],
[ "esp:D27", "r4:1", "yellow", [] ],
[ "r4:2", "led4:A", "yellow", [] ],
[ "led4:C", "esp:GND.2", "black", [] ],
[ "esp:D25", "r5:1", "red", [] ],
[ "r5:2", "rgb_red:A", "red", [] ],
[ "rgb_red:C", "esp:GND.2", "black", [] ],
[ "esp:D26", "r6:1", "green", [] ],
[ "r6:2", "rgb_green:A", "green", [] ],
[ "rgb_green:C","esp:GND.2", "black", [] ],
[ "esp:D33", "r7:1", "blue", [] ],
[ "r7:2", "rgb_blue:A", "blue", [] ],
[ "rgb_blue:C", "esp:GND.2", "black", [] ],
[ "esp:D32", "bz1:1", "magenta",[] ],
[ "bz1:2", "esp:GND.2", "black", [] ]
],
"dependencies": {}
}
Voice Commands Reference
Type any of these commands into the Wokwi Serial Monitor (press Enter to send):
Run the Wokwi Simulation
Go to wokwi.com, click New Project, and choose ESP32 as your board.
Delete the default code and paste the full Arduino sketch from Step 06. Save the file as voice_home_assistant.ino.
Click the diagram.json tab in Wokwi, select all, and paste the JSON from Step 07. All components will appear automatically wired.
Click the Library Manager icon in Wokwi (or add to libraries.txt): add LiquidCrystal I2C and DHT sensor library.
Click the ▶ Play button. Once the Serial Monitor shows "System Ready!", type a command like turn on light and press Enter. Watch the LED light up and the LCD update!
Comments
Post a Comment