Menyalakan Lampu AC dengan Gerakan Tangan: ESP32, OpenCV, dan MQTT

Proyek ESP32 OpenCV MQTT ini menggabungkan kecerdasan buatan dan IoT: kamera laptop mendeteksi gerakan tangan dengan OpenCV dan MediaPipe, program Python mengirim perintah ON/OFF lewat MQTT, lalu ESP32 menyalakan atau mematikan lampu AC melalui relay. Tutorial ini sudah diperbarui dengan kode yang teruji (paho-mqtt 2.x, MediaPipe versi yang kompatibel) serta pin relay yang aman untuk ESP32.
⚠️ Peringatan keselamatan listrik: tegangan AC 220 V berbahaya dan dapat mematikan. Semua sambungan AC harus dikerjakan oleh teknisi atau orang dewasa berpengalaman, memakai modul relay opto-isolator, dan ditempatkan di kotak tertutup. Untuk latihan, gunakan LED atau beban DC terlebih dahulu.
Daftar isi:
- Cara kerja sistem
- Wiring ESP32 ke relay
- Program Python: deteksi gerakan tangan
- Program ESP32: kendali relay
- Uji coba
- Troubleshooting
Cara Kerja Sistem ESP32 OpenCV MQTT
- Kamera laptop merekam tangan; MediaPipe Hands mendeteksi 21 titik (landmark) pada tangan.
- Program Python menghitung jari yang terbuka: 4 jari terbuka = ON, tangan mengepal = OFF.
- Perintah dikirim ke topik MQTT
bisaioti/rumah/lampu/ruang_tamu/set— hanya saat status berubah. - ESP32 yang berlangganan topik tersebut menggerakkan relay, lalu melaporkan status ke topik
.../state.
Konsep publish/subscribe yang dipakai di sini dijelaskan di panduan protokol MQTT.
Alat dan Bahan
- ESP32 DevKit dan kabel USB data.
- Modul relay opto-isolator 1–4 channel (5 V, aktif LOW).
- Lampu AC + fitting + kabel, atau LED/beban DC untuk latihan aman.
- Kabel jumper male-female.
- Laptop/PC dengan webcam, Python 3.10–3.12, dan Arduino IDE.
- Broker MQTT: broker publik
test.mosquitto.orguntuk uji coba, atau Mosquitto lokal (disarankan).
Wiring ESP32 ke Modul Relay

| ESP32 | Modul relay |
|---|---|
| VIN (5 V) | VCC |
| GND | GND |
| GPIO26 | IN1 |
| GPIO27 | IN2 |
| GPIO14 | IN3 |
| GPIO25 | IN4 |
Versi lama tutorial ini memakai GPIO12 untuk IN4. GPIO12 adalah strapping pin yang menentukan tegangan flash saat boot; modul relay yang menarik pin ini ke HIGH bisa membuat ESP32 gagal boot atau gagal upload. Karena itu IN4 dipindah ke GPIO25.
Sisi AC: kabel fase (L) diputus melalui relay — sambungkan ke terminal COM, lalu dari NO (Normally Open) ke lampu. Kabel netral (N) langsung ke lampu. Semua sambungan AC wajib berada di dalam kotak tertutup.
Instalasi Software
Arduino IDE: instal board ESP32 lewat Boards Manager (URL https://espressif.github.io/arduino-esp32/package_esp32_index.json) dan library PubSubClient.
Python: buat virtual environment lalu instal library:
python -m venv venv
# Windows: venv\Scripts\activate | Linux/macOS: source venv/bin/activate
pip install opencv-python "mediapipe==0.10.21" "paho-mqtt>=2.0"Penting: MediaPipe versi 0.10.30 ke atas menghapus API mp.solutions yang dipakai program ini, sehingga muncul error module 'mediapipe' has no attribute 'solutions'. Karena itu versi dikunci ke 0.10.21 (mendukung Python 3.9–3.12). paho-mqtt 2.x juga mewajibkan parameter CallbackAPIVersion saat membuat client.
Program Python OpenCV: Deteksi Gerakan Tangan dan Kirim MQTT ke ESP32
Simpan sebagai hand_control.py. Jika memakai broker publik, ganti topik dengan nama unik agar tidak bentrok dengan pengguna lain.
import time
import uuid
import cv2
import mediapipe as mp
import paho.mqtt.client as mqtt
# ====== KONFIGURASI ======
BROKER = "test.mosquitto.org" # atau IP komputer yang menjalankan Mosquitto
PORT = 1883
TOPIC_SET = "bisaioti/rumah/lampu/ruang_tamu/set" # buat unik jika memakai broker publik
JEDA_MIN = 1.0 # jeda minimal antar perintah (detik)
# =========================
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"gesture-pc-{uuid.uuid4().hex[:6]}")
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
def hitung_jari(hand):
"""Hitung jari telunjuk-kelingking yang terbuka (ujung jari di atas sendi)."""
lm = hand.landmark
jari = 0
for ujung, sendi in [(8, 6), (12, 10), (16, 14), (20, 18)]:
if lm[ujung].y < lm[sendi].y:
jari += 1
return jari
cap = cv2.VideoCapture(0)
state_terakhir, waktu_kirim = None, 0.0
with mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.7,
min_tracking_confidence=0.5) as hands:
while cap.isOpened():
ok, frame = cap.read()
if not ok:
break
frame = cv2.flip(frame, 1)
hasil = hands.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
gesture = None
if hasil.multi_hand_landmarks:
hand = hasil.multi_hand_landmarks[0]
mp_draw.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)
jari = hitung_jari(hand)
if jari >= 4:
gesture = "ON" # telapak terbuka
elif jari == 0:
gesture = "OFF" # tangan mengepal
# kirim hanya jika status berubah, agar broker tidak dibanjiri pesan
if gesture and gesture != state_terakhir and time.time() - waktu_kirim > JEDA_MIN:
client.publish(TOPIC_SET, gesture, qos=1, retain=True)
state_terakhir, waktu_kirim = gesture, time.time()
print("Kirim perintah:", gesture)
cv2.putText(frame, f"Lampu: {state_terakhir or '-'}", (10, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow("Kontrol Lampu dengan Gerakan Tangan (tekan q)", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
client.loop_stop()
client.disconnect()hitung_jari()membandingkan posisi ujung jari dengan sendinya; jika ujung lebih tinggi, jari dianggap terbuka.- Perintah hanya dikirim ketika status berubah dan minimal berjarak 1 detik, sehingga lampu tidak berkedip dan broker tidak dibanjiri pesan.
- Pesan dikirim dengan
retain=True, sehingga ESP32 yang baru menyala langsung menerima perintah terakhir. - Client ID dibuat acak dengan
uuidagar tidak bentrok dengan client lain.
Program ESP32: Menerima Perintah MQTT dari OpenCV dan Mengendalikan Relay
#include <WiFi.h>
#include <PubSubClient.h>
// ====== KONFIGURASI ======
const char* WIFI_SSID = "NAMA_WIFI";
const char* WIFI_PASSWORD = "PASSWORD_WIFI";
const char* MQTT_SERVER = "test.mosquitto.org"; // samakan dengan program Python
const int MQTT_PORT = 1883;
const char* TOPIC_SET = "bisaioti/rumah/lampu/ruang_tamu/set";
const char* TOPIC_STATE = "bisaioti/rumah/lampu/ruang_tamu/state";
// Relay aktif LOW (umum pada modul relay opto-isolator)
const int RELAYS[] = {26, 27, 14, 25}; // hindari GPIO12 (strapping pin)
const int JUMLAH_RELAY = 4;
// =========================
WiFiClient espClient;
PubSubClient mqtt(espClient);
String clientId;
unsigned long lastReconnect = 0;
void setLampu(bool nyala) {
for (int i = 0; i < JUMLAH_RELAY; i++) digitalWrite(RELAYS[i], nyala ? LOW : HIGH);
mqtt.publish(TOPIC_STATE, nyala ? "ON" : "OFF", true); // laporkan status (retained)
Serial.printf("Lampu %s\n", nyala ? "MENYALA" : "MATI");
}
void callback(char* topic, byte* payload, unsigned int length) {
String msg;
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
msg.trim();
Serial.printf("Perintah masuk: %s\n", msg.c_str());
if (msg == "ON") setLampu(true);
if (msg == "OFF") setLampu(false);
}
void connectMQTT() {
Serial.print("Menghubungkan ke broker ... ");
if (mqtt.connect(clientId.c_str())) {
Serial.println("berhasil");
mqtt.subscribe(TOPIC_SET, 1); // perintah terakhir (retained) langsung diterima
} else {
Serial.printf("gagal, state=%d\n", mqtt.state());
}
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < JUMLAH_RELAY; i++) {
pinMode(RELAYS[i], OUTPUT);
digitalWrite(RELAYS[i], HIGH); // semua relay OFF saat mulai
}
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi terhubung");
String mac = WiFi.macAddress();
mac.replace(":", "");
clientId = "esp32-relay-" + mac.substring(6);
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
mqtt.setCallback(callback);
}
void loop() {
if (!mqtt.connected()) {
if (lastReconnect == 0 || millis() - lastReconnect > 5000) {
lastReconnect = millis();
connectMQTT();
}
return;
}
mqtt.loop();
}- Relay dinyalakan dengan
LOWkarena modul relay opto-isolator umumnya aktif LOW; semua relay dimatikan saat ESP32 mulai. - Subscribe dilakukan sekali setiap kali terhubung, dan reconnect berjalan tanpa memblokir program.
- Status lampu dilaporkan ke topik
.../statesehingga bisa ditampilkan di dashboard. Contoh dashboard ada di tutorial ESP32 DHT11 Node-RED.
Uji Coba Sistem ESP32, OpenCV, dan MQTT
- Upload program ke ESP32 dan buka Serial Monitor (115200). Pastikan muncul WiFi terhubung dan Menghubungkan ke broker … berhasil.
- Jalankan
python hand_control.py. Jendela kamera akan terbuka. - Buka telapak tangan (4 jari terbuka) → lampu menyala. Kepalkan tangan → lampu mati. Tekan q untuk keluar.
- Untuk memantau pesan, jalankan
mosquitto_sub -h test.mosquitto.org -t "bisaioti/rumah/lampu/#" -v.
Troubleshooting ESP32 OpenCV MQTT
| Gejala | Penyebab | Solusi |
|---|---|---|
module 'mediapipe' has no attribute 'solutions' | MediaPipe versi baru (0.10.30+) | pip install "mediapipe==0.10.21" dengan Python 3.9–3.12 |
Error saat mqtt.Client(...) | Kode lama paho-mqtt 1.x | Gunakan mqtt.CallbackAPIVersion.VERSION2 seperti di atas |
| Kamera tidak terbuka | Indeks kamera salah atau dipakai aplikasi lain | Ganti VideoCapture(0) menjadi 1, tutup aplikasi lain |
| ESP32 gagal boot/upload setelah relay dipasang | Relay terhubung ke strapping pin (GPIO12/GPIO2/GPIO15) | Pakai pin 26, 27, 14, 25 seperti tabel wiring |
| Relay bekerja terbalik | Modul relay aktif HIGH | Tukar LOW dan HIGH di fungsi setLampu() |
| Perintah tidak sampai | Topik atau broker berbeda | Samakan topik dan broker di Python dan ESP32 |
Pengembangan Lanjutan
- Tambahkan gesture lain (misalnya 1 jari = lampu 1, 2 jari = lampu 2) untuk mengendalikan tiap relay secara terpisah.
- Gunakan broker pribadi dengan login dan TLS agar orang lain tidak bisa mengirim perintah — lihat tutorial HiveMQ Cloud.
- Integrasikan dengan Node-RED atau Home Assistant untuk tombol manual dan jadwal otomatis.
📌 Baca juga: Waveshare ESP32-S3-Relay-1CH: pinout, wiring & program
Kesimpulan
Dengan OpenCV, MediaPipe, MQTT, dan ESP32, Anda sudah membangun sistem AI + IoT sederhana: gerakan tangan → perintah MQTT → relay → lampu AC. Pola yang sama bisa dipakai untuk mengendalikan kipas, pompa, atau perangkat lain. Pelajari dasar komunikasinya di tutorial MQTT ESP32 dan tutorial MQTT Python, atau ikuti Training ESP32 Berbasis IoT.
Referensi
- Google, “MediaPipe Hands (legacy solution)” – mediapipe.readthedocs.io
- Eclipse Paho, “paho-mqtt” – pypi.org
- Espressif, “ESP32 Strapping Pins” – docs.espressif.com
Baca juga: ESP32: Panduan Komprehensif untuk Pemula hingga Ahli (IoT, Pemrograman, & Proyek).



