Pendahuluan — Dari On/Off ke Sudut Presisi
Di Seri 1, aktuator paling sering dipakai adalah modul relay — lampu hanya ON atau OFF lewat MQTT (artikel #8). Itu cukup untuk smart switch, tapi tidak cukup untuk gerakan bertahap: buka pintu setengah, atur sudut kamera, atau posisi flap greenhouse.
Artikel Tier 2 ini mengajarkan servo SG90 dan dasar PWM di ESP32: putar lengan servo ke sudut 0–180° dari perintah MQTT — pola yang melengkapi relay di proyek gabungan (#9) dan capstone #10.
Prasyarat: Paham GPIO dasar (#3), MQTT subscribe (#7), dan sudah pernah wiring relay #8. Broker sendiri Mosquitto (#16) dipakai di sketch ini.
Relay vs Servo vs PWM
| Aktuator | Kontrol | MQTT payload | Cocok untuk |
|---|---|---|---|
| Relay (#8) | Digital on/off | ON / OFF | Lampu, pompa, kipas on/off |
| Servo SG90 | Sudut 0–180° | 0 … 180 | Flap, lengan robot, throttle |
| PWM / LEDC | Duty cycle 0–100% | Angka atau persen | Dimmer, kecepatan DC motor kecil |
Servo SG90 secara internal sudah memakai PWM 50 Hz — library ESP32Servo menyembunyikan detail LEDC. Kamu cukup panggil servo.write(sudut).
Hardware — SG90 & ESP32
- Micro servo SG90 (9g, 4,8–6 V)
- ESP32 DevKit
- Kabel jumper · breadboard opsional
- Power supply 5 V eksternal disarankan jika servo + WiFi bersamaan (servo bisa tarik hingga ~650 mA saat stall)
Wiring SG90 ke ESP32
| Pin SG90 | Warna umum | Ke ESP32 / power |
|---|---|---|
| GND | Coklat | GND ESP32 |
| VCC | Merah | 5 V (USB/VIN — atau supply 5 V terpisah) |
| Signal (PWM) | Oranye | GPIO 27 |
Kita pakai GPIO 27 agar tidak bentrok dengan GPIO 26 relay (#8) dan GPIO 4 DHT22 (#5). Hindari GPIO 6–11 (flash internal).
Pro tip: Jika servo bergetar di sudut tertentu, sambungkan kapasitor 100 µF antara VCC dan GND dekat servo — mengurangi noise saat WiFi transmit.
Apa Itu PWM di ESP32?
PWM (Pulse Width Modulation) mengubah lebar pulsa dalam satu periode tetap. Servo standar mengharapkan pulsa 50 Hz (periode 20 ms):
- ~1 ms pulse → sudut ~0°
- ~1,5 ms pulse → sudut ~90°
- ~2 ms pulse → sudut ~180°
ESP32 punya periferal LEDC (LED PWM controller) untuk generate sinyal ini. Library ESP32Servo mengalokasikan timer LEDC secara otomatis.
Library & Dependensi
Di Arduino IDE, install dari Library Manager:
- ESP32Servo oleh Kevin Harrington
- PubSubClient + WiFi (sudah familiar dari #7/#8)
Di PlatformIO (#29):
lib_deps =
madhephaestus/ESP32Servo
knolleary/PubSubClient
Broker & Topic MQTT
Mengikuti konvensi Seri 2 — broker pribadi 192.168.1.50, user kindo_esp32:
- Topic sudut servo:
kodingindonesia/esp32/servo/sudut - Payload: angka bulat
0–180(derajat) - Topic relay (referensi #8):
kodingindonesia/esp32/lampu/kontrol—ON/OFF
Sketch Dasar — Sweep Lokal (Tanpa MQTT)
Uji hardware dulu sebelum WiFi:
#include <ESP32Servo.h>
#define SERVO_PIN 27
Servo servo;
void setup() {
Serial.begin(115200);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo.setPeriodHertz(50);
servo.attach(SERVO_PIN, 500, 2400);
servo.write(90);
Serial.println("Servo siap — sweep 0-180");
}
void loop() {
for (int a = 0; a <= 180; a += 10) {
servo.write(a);
Serial.printf("Sudut: %d\n", a);
delay(300);
}
delay(1000);
}
Sketch Lengkap — MQTT Subscribe Sudut
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#define SERVO_PIN 27
const char* ssid = "GANTI_NAMA_WIFI";
const char* pass = "GANTI_PASSWORD_WIFI";
const char* mqtt_host = "192.168.1.50";
const char* mqtt_user = "kindo_esp32";
const char* mqtt_pass = "GANTI_PASSWORD_MQTT";
const char* topic_servo = "kodingindonesia/esp32/servo/sudut";
Servo servo;
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
int currentAngle = 90;
void onMessage(char* topic, byte* payload, unsigned int len) {
char msg[8];
size_t n = len < sizeof(msg) - 1 ? len : sizeof(msg) - 1;
memcpy(msg, payload, n);
msg[n] = '\0';
int angle = atoi(msg);
angle = constrain(angle, 0, 180);
currentAngle = angle;
servo.write(angle);
Serial.printf("MQTT → sudut %d\n", angle);
}
void reconnectMqtt() {
while (!mqtt.connected()) {
if (mqtt.connect("kindo-esp32-servo", mqtt_user, mqtt_pass)) {
mqtt.subscribe(topic_servo);
Serial.println("MQTT connected + subscribe servo/sudut");
} else {
delay(2000);
}
}
}
void setup() {
Serial.begin(115200);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo.setPeriodHertz(50);
servo.attach(SERVO_PIN, 500, 2400);
servo.write(90);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) { delay(300); }
mqtt.setServer(mqtt_host, 1883);
mqtt.setCallback(onMessage);
reconnectMqtt();
}
void loop() {
if (!mqtt.connected()) reconnectMqtt();
mqtt.loop();
}
Payload MQTT mengikuti pola JSON sensor di topic kodingindonesia/esp32/dht22/data — di sini sengaja angka mentah agar mudah dikirim dari mosquitto_pub atau Node-RED (#23). Untuk timestamp di log, lihat NTP (#34) — contoh unix 1782977400 = 2026-07-02T14:30:00 UTC.
Mapping Sudut ke Skenario Nyata
Beberapa contoh mapping praktis untuk proyek IoT:
| Sudut MQTT | Contoh mekanik | Proyek terkait |
|---|---|---|
0 | Flap tertutup penuh | Greenhouse capstone (#39) |
90 | Posisi netral / setengah buka | Kamera tilt |
180 | Flap terbuka penuh | Ventilasi maksimum |
Simpan mapping di dokumentasi tim — MQTT hanya mengirim angka; arti mekanik ditentukan oleh instalasi fisik.
Integrasi Home Assistant (Opsional)
Di Home Assistant (#21), buat MQTT number atau slider:
- Topic state:
kodingindonesia/esp32/servo/sudut - Min: 0 · Max: 180 · Step: 1
- Mode: slider di dashboard — gerakkan servo dari UI HA
Berbeda dengan switch relay lampu yang hanya dua state — slider HA langsung memetakan ke servo.write() di firmware.
Uji dengan mosquitto_pub
Dari laptop yang bisa reach broker 192.168.1.50:
mosquitto_pub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/servo/sudut -m 0
mosquitto_pub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/servo/sudut -m 90
mosquitto_pub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/servo/sudut -m 180
Servo harus bergerak ke posisi yang dikirim. Bandingkan dengan relay: mosquitto_pub … -t kodingindonesia/esp32/lampu/kontrol -m ON hanya dua state.
Kombinasi Servo + Relay di Satu Proyek
Sketch gabungan bisa subscribe dua topic:
…/lampu/kontrol→ relay GPIO 26 (#8)…/servo/sudut→ servo GPIO 27 (artikel ini)
Di callback MQTT, bandingkan strcmp(topic, …) untuk route perintah. Saat proyek membesar, pertimbangkan FreeRTOS (#31) — task servo terpisah dari task sensor DHT22.
LEDC Manual (Opsional)
Jika butuh PWM murni tanpa servo (mis. dimmer LED), pakai ledcSetup + ledcWrite langsung — tanpa library servo. Servo SG90 lebih praktis lewat ESP32Servo karena kalibrasi pulse width sudah di-handle.
Gerakan Bertahap (Anti-Jerk)
Memanggil servo.write(180) langsung dari 0 bisa membuat lengan servo “loncat” — mekanik flap atau kamera tilt terasa kasar. Untuk demo yang lebih halus, ramp sudut per langkah kecil di firmware:
void moveServoSmooth(int target) {
target = constrain(target, 0, 180);
int step = (target > currentAngle) ? 2 : -2;
while (currentAngle != target) {
currentAngle += step;
if ((step > 0 && currentAngle > target) ||
(step < 0 && currentAngle < target)) {
currentAngle = target;
}
servo.write(currentAngle);
delay(15); // ~75–150 ms total untuk lonjakan 90°
}
}
Panggil moveServoSmooth(angle) dari callback MQTT menggantikan servo.write(angle) langsung. Di produksi, pertimbangkan millis() non-blocking agar mqtt.loop() tetap responsif — pola yang sama dengan task terpisah di FreeRTOS (#31).
Untuk flap greenhouse (#39), ramp 2° per 20 ms sering cukup: flap tidak “membanting” engsel plastik, tapi tetap merespons perintah MQTT dalam waktu kurang dari dua detik untuk perjalanan penuh 0→180°.
Kalibrasi Pulse Width SG90
Parameter servo.attach(SERVO_PIN, 500, 2400) memetakan sudut ke lebar pulsa dalam mikrodetik. SG90 murah kadang tidak mencapai 0° atau 180° nominal — jika lengan mentok sebelum angka MQTT, sesuaikan:
- 0° terlalu dalam: naikkan nilai minimum (mis. 600 µs bukan 500)
- 180° tidak penuh: naikkan maksimum (mis. 2500 µs)
- Uji dengan
mosquitto_pub -m 0dan-m 180sambil amati mekanik — jangan paksa gear saat mentok
Catat nilai kalibrasi di README proyek tim; MQTT tetap mengirim 0–180, firmware yang menerjemahkan ke pulse width fisik.
Daya, Stall Current & Mekanik
- SG90 stall current ~650 mA — USB ESP32 saja bisa drop voltage saat servo + WiFi peak
- Gunakan supply 5 V 1 A terpisah untuk servo; GND servo dan GND ESP32 harus common
- Jangan memaksa lengan servo saat motor aktif — bisa rusak gear plastik
- Servo continuous rotation (360°) berbeda — butuh library/kalibrasi lain
Keamanan & Produksi
- Ganti
GANTI_NAMA_WIFI,GANTI_PASSWORD_WIFI,GANTI_PASSWORD_MQTT— jangan commit ke GitHub - Validasi sudut di firmware:
constrain(angle, 0, 180)— jangan percaya payload MQTT mentah - Pakai MQTT TLS (#17) jika perintah servo mengontrol perangkat fisik berisiko
- Topic kontrol servo sebaiknya tidak retained dengan sudut acak — hindari gerakan tak terduga saat boot
Estimasi Biaya
| Komponen | Harga perkiraan (IDR) |
|---|---|
| ESP32 DevKit | 35.000 – 55.000 |
| SG90 servo | 18.000 – 30.000 |
| Breadboard + jumper | 10.000 – 20.000 |
| Total | ~63.000 – 105.000 |
Checklist Sebelum Demo
- ☐ Sweep lokal 0→180→90 berjalan halus
- ☐ WiFi connect + MQTT subscribe
servo/sudut - ☐
mosquitto_pub -m 0,90,180menggerakkan servo - ☐ GPIO 27 signal · GND common · tidak bentrok GPIO relay/DHT
- ☐ Serial mencetak sudut yang diterima
Node-RED & Dashboard Visual
Di Node-RED (#23), tambahkan node ui_slider yang publish ke kodingindonesia/esp32/servo/sudut. Slider 0–180 di browser langsung menggerakkan SG90 — pola mirip tombol ON/OFF relay di dashboard yang sama, tapi dengan kontrol analog.
Kombinasikan dengan chart suhu dari topic kodingindonesia/esp32/dht22/data — misalnya buka flap (sudut 120) otomatis saat suhu > 30°C lewat function node. Itu pratinjau logika capstone greenhouse (#39).
Publish Balik Sudut (Opsional)
Untuk monitoring di Grafana (#19), ESP32 bisa publish sudut terakhir ke topic kodingindonesia/esp32/servo/status setelah setiap perubahan — payload sama angka 0–180. Subscriber Python (#18) bisa menyimpan histori posisi aktuator, bukan hanya sensor.
FAQ Singkat
- Servo tidak bergerak sama sekali?
- Cek VCC 5 V, GND common, dan pin signal GPIO 27. Uji sketch sweep lokal dulu.
- Servo bergetar di satu sudut?
- Normal di beban berat — kurangi beban mekanik atau naikkan supply current.
- Bisa kontrol dari Home Assistant?
- Ya — buat MQTT number/slider ke topic
servo/sudutseperti switch relay di #21. - MG996R vs SG90?
- MG996R lebih kuat tapi butuh arus lebih besar — wiring MQTT sama, perhatikan power supply.
Tips & Troubleshooting
- ESP32 reset saat servo gerak: Power supply lemah — pisahkan 5 V servo atau tambah kapasitor
- MQTT tidak mengubah sudut: Cek
mqtt.setCallbacksebelumconnect; pastikanmqtt.loop()diloop() - Sudut tidak akurat: Kalibrasi
attach(pin, minUs, maxUs)— SG90 umum 500–2400 µs - Topic salah: Harus persis
kodingindonesia/esp32/servo/sudut— case sensitive - Payload bukan angka:
atoimengembalikan 0 — kirim45bukan"sudut":45di artikel ini - Konflik timer PWM: Pastikan
allocateTimer(0..3)dipanggil sekali disetup()
Langkah Selanjutnya — Tier 2 Seri 2
Servo melengkapi relay on/off — sekarang aktuator punya gerakan presisi. Lanjut ke pelengkap Tier 2:
- ADC Soil Moisture & LDR (#35): sensor analog — melengkapi servo dengan input tanah & cahaya
- NTP (#34) — timestamp di log pergerakan servo
- BLE (#32) — kontrol servo dari app HP (lanjutan)
- Grafana (#19) — grafik histori sudut jika publish balik ke MQTT
- Capstone greenhouse (#39) — servo flap + pompa relay + sensor
PWM membuka gerakan halus di proyek ESP32 — lanjutkan di halaman artikel Koding Indonesia.