Pendahuluan — OOP yang sama, RAM yang beda
Di Seri 3 kamu sudah nyaman dengan class, composition, dan pattern ringan di Factory & Strategy (#50) serta Capstone (#49). Artikel ini jembatan ke perangkat: MicroPython di ESP32 — OOP-nya tetap Python, tapi memori dan modul hardware memaksa desain lebih hemat.
Pola yang sama dengan firmware yang membesar di capstone smart greenhouse (#39): layanan punya sensor dan aktuator, bukan satu file spaghetti GPIO.
Prasyarat: Factory & Strategy (#50) atau minimal Composition (#47) + Mengenal OOP (#40). Familiar ESP32: Mengenal ESP32 (#1) · Greenhouse (#39). Opsional: Python di ekosistem Kindo lewat Subscriber MQTT (#18).
CPython vs MicroPython — apa yang berubah?
| Aspek | CPython (Seri 3) | MicroPython (ESP32) |
|---|---|---|
| RAM / flash | Longgar | Ketat — hindari object berlebihan |
@dataclass |
Stdlib lengkap | Sering tidak ada / terbatas — class biasa lebih aman |
| Hardware | Tidak ada | machine.Pin, ADC, I2C, WiFi |
| OOP inti | class, inheritance, composition | Sama — mental model Seri 3 tetap berlaku |
Jangan buang composition hanya karena “firmware”. Justru di node IoT, Composition (#47) menjaga GPIO tidak tercecer di mana-mana.
Node punya Sensor dan Led — pola yang sama dengan “service punya koleksi” di Seri 3.Stub PC — belajar tanpa board di tangan
Contoh di bawah memakai stub FakePin agar bisa dijalankan di PC (CPython) untuk latihan. Di ESP32 ganti menjadi from machine import Pin. Ini sengaja: audit & belajar tidak bergantung pada hardware.
class FakePin:
"""Pengganti machine.Pin untuk latihan di PC."""
OUT = 1
IN = 0
def __init__(self, nomor, mode=OUT):
self.nomor = nomor
self.mode = mode
self._nilai = 0
def value(self, v=None):
if v is None:
return self._nilai
self._nilai = 1 if v else 0
return self._nilai
pin = FakePin(2, FakePin.OUT)
pin.value(1)
print("pin", pin.nomor, "=>", pin.value())
Output:
pin 2 => 1
Class Led — encapsulate GPIO
Jangan sebar pin.value(1) di loop utama. Bungkus di class (semangat Encapsulation (#43)):
class FakePin:
OUT = 1
def __init__(self, nomor, mode=OUT):
self.nomor = nomor
self._nilai = 0
def value(self, v=None):
if v is None:
return self._nilai
self._nilai = 1 if v else 0
return self._nilai
class Led:
def __init__(self, pin):
self._pin = pin
def nyala(self):
self._pin.value(1)
def mati(self):
self._pin.value(0)
def status(self):
return "ON" if self._pin.value() else "OFF"
led = Led(FakePin(2))
led.nyala()
print(led.status())
led.mati()
print(led.status())
Output:
ON
OFF
Class Sensor — baca + label
Sensor di MicroPython sering ADC atau DHT. Untuk latihan PC kita simulasi nilai:
class SensorSuhu:
def __init__(self, nama, baca_fn):
self.nama = nama
self._baca_fn = baca_fn
def baca(self):
return float(self._baca_fn())
def label(self, nilai=None):
if nilai is None:
nilai = self.baca()
return f"{self.nama}: {nilai:.1f} C"
def simulasi_dht():
return 27.5
s = SensorSuhu("DHT22", simulasi_dht)
print(s.label())
Output:
DHT22: 27.5 C
Parameter opsional nilai disiapkan sejak dini: nanti di Node.tick() kita teruskan hasil satu bacaan agar sensor tidak dibaca dua kali. Di board nyata, baca_fn bisa memanggil driver DHT — pola callback/composition ini selaras dengan “satu antarmuka, banyak bentuk” di Polymorphism (#45).
Node — composition di firmware
Node tidak mewarisi Led; ia punya sensor dan led:
class FakePin:
OUT = 1
def __init__(self, nomor, mode=OUT):
self.nomor = nomor
self.mode = mode
self._nilai = 0
def value(self, v=None):
if v is None:
return self._nilai
self._nilai = 1 if v else 0
return self._nilai
class Led:
def __init__(self, pin):
self._pin = pin
def nyala(self):
self._pin.value(1)
def mati(self):
self._pin.value(0)
def status(self):
return "ON" if self._pin.value() else "OFF"
class SensorSuhu:
def __init__(self, nama, baca_fn):
self.nama = nama
self._baca_fn = baca_fn
def baca(self):
return float(self._baca_fn())
def label(self, nilai=None):
# Jangan panggil baca() lagi jika nilai sudah ada —
# penting saat baca_fn mahal / side-effect (pop, I2C).
if nilai is None:
nilai = self.baca()
return f"{self.nama}: {nilai:.1f} C"
class Node:
def __init__(self, nama, sensor, led, ambang=30.0):
self.nama = nama
self.sensor = sensor
self.led = led
self.ambang = ambang
def tick(self):
suhu = self.sensor.baca()
if suhu >= self.ambang:
self.led.nyala()
else:
self.led.mati()
return f"{self.nama} | {self.sensor.label(suhu)} | LED {self.led.status()}"
def baca():
return 31.2
node = Node("Kebun-A", SensorSuhu("DHT22", baca), Led(FakePin(2)), ambang=30.0)
print(node.tick())
Output:
Kebun-A | DHT22: 31.2 C | LED ON
Satu tick() = satu baca(). Label memakai nilai yang sudah dibaca, bukan memanggil sensor lagi.
Porting singkat ke ESP32
Setelah demo() hijau di PC, flash ke board. Ganti stub dengan modul hardware — antarmuka class Led/Node tetap sama (semangat Encapsulation (#43)):
# MicroPython di ESP32
from machine import Pin
led = Led(Pin(2, Pin.OUT))
# SensorSuhu: isi baca_fn dengan driver DHT/ADC kamu
# node = Node("Kebun-A", sensor, led, ambang=30.0)
# while True:
# print(node.tick())
Jangan biarkan nomor GPIO tersebar di loop utama. Kalau ImportError: machine muncul di desktop, itu tanda kamu menjalankan kode board di CPython — kembali ke FakePin untuk latihan.
Pola Dasar — OOP di node MicroPython
-
1
Bungkus pin di class kecil
Led/Sensor— jangan biarkan nomor GPIO tersebar. -
2
Susun Node dengan composition
Nodepunya sensor + aktuator — pola Composition (#47). -
3
Hemat fitur bahasa Class biasa dulu; dataclass (#48) / ABC (#46) berat boleh ditunda di MCU kecil.
-
4
Uji di PC dengan stub
FakePin/ fungsi simulasi — ganti kemachinesaat flash. -
5
Hubungkan ke Seri IoT MQTT/dashboard: lihat Greenhouse (#39) dan Deep Sleep DHT22 (#11).
Kode lengkap — node_micropython_oop.py
Simpan dan jalankan di PC: python node_micropython_oop.py. Di ESP32, ganti FakePin dengan machine.Pin (lihat komentar).
"""OOP ringan untuk node IoT — stub PC (Tier 2 #51).
Di ESP32: from machine import Pin # lalu Pin(2, Pin.OUT)
"""
from __future__ import annotations
class FakePin:
OUT = 1
IN = 0
def __init__(self, nomor: int, mode: int = OUT) -> None:
self.nomor = nomor
self.mode = mode
self._nilai = 0
def value(self, v=None):
if v is None:
return self._nilai
self._nilai = 1 if v else 0
return self._nilai
class Led:
def __init__(self, pin: FakePin) -> None:
self._pin = pin
def nyala(self) -> None:
self._pin.value(1)
def mati(self) -> None:
self._pin.value(0)
def status(self) -> str:
return "ON" if self._pin.value() else "OFF"
class SensorSuhu:
def __init__(self, nama: str, baca_fn) -> None:
self.nama = nama
self._baca_fn = baca_fn
def baca(self) -> float:
return float(self._baca_fn())
def label(self, nilai: float | None = None) -> str:
if nilai is None:
nilai = self.baca()
return f"{self.nama}: {nilai:.1f} C"
class Node:
def __init__(self, nama: str, sensor: SensorSuhu, led: Led, ambang: float = 30.0) -> None:
self.nama = nama
self.sensor = sensor
self.led = led
self.ambang = ambang
def tick(self) -> str:
suhu = self.sensor.baca()
if suhu >= self.ambang:
self.led.nyala()
else:
self.led.mati()
return f"{self.nama} | {self.sensor.label(suhu)} | LED {self.led.status()}"
def demo() -> None:
nilai = [28.0, 31.5, 29.0]
def baca():
return nilai.pop(0) if nilai else 29.0
node = Node("Kebun-A", SensorSuhu("DHT22", baca), Led(FakePin(2)), ambang=30.0)
for _ in range(3):
print(node.tick())
if __name__ == "__main__":
demo()
Output yang diharapkan:
Kebun-A | DHT22: 28.0 C | LED OFF
Kebun-A | DHT22: 31.5 C | LED ON
Kebun-A | DHT22: 29.0 C | LED OFF
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan |
|---|---|---|
| MemoryError di MCU | Terlalu banyak object / string besar | Kurangi alokasi; class tipis; hindari import berat |
| GPIO “aneh” di banyak file | Nomor pin hardcode tersebar | Bungkus di Led/Sensor; Node yang orkestrasi |
ImportError: machine di PC |
Menjalankan kode ESP32 di CPython | Pakai stub FakePin saat belajar di desktop |
| Memaksa dataclass/ABC penuh | Port MicroPython terbatas | Class biasa + composition cukup untuk node kecil |
Warisi Node(Led) |
Salah inheritance | Composition: Node punya Led |
| Nilai sensor “loncat” / LED salah | label() memanggil baca() lagi di dalam tick() |
Satu bacaan per tick — teruskan nilai ke label(suhu) |
Latihan singkat
- Tambah class
RelaymiripLed(semangat Encapsulation (#43)), lalu masukkan keNode(composition). - Ubah
demo()agar LED nyala hanya jika suhu > ambang dan dua tick berturut-turut panas. - Sketsa: bagaimana
Node.tick()bisa menerbitkan JSON ke MQTT (lihat pola topik di Deep Sleep (#11)) — tanpa wajib implementasi penuh.
FAQ singkat
Apakah harus MicroPython, bukan Arduino C++?
Tidak wajib. Artikel ini untuk pembaca Seri 3 yang ingin OOP Python di board. Firmware C++ tetap valid di Seri ESP32.
Apakah Factory/Strategy (#50) dipakai di MCU?
Boleh jika cabang jenis sensor mulai liar. Untuk satu DHT + satu LED, composition sederhana lebih dulu.
Type hint float | None aman di ESP32?
Blok lengkap memakai from __future__ import annotations untuk latihan di PC (CPython 3.10+). Saat flash MicroPython, type hint boleh dihapus — perilaku runtime tidak bergantung padanya.
Di mana WiFi/MQTT?
Di luar scope singkat ini. Sambungkan ke alur Greenhouse (#39) setelah struktur class rapi.
Lanjut ke mana?
Lanjut Tier 2: Dari OOP ke Web — Class di Flask / FastAPI (#52) — service + handler tipis sebagai pintu Web Dev.
Kesimpulan & langkah berikutnya
OOP Seri 3 tidak hilang di MicroPython — yang berubah adalah anggaran memori dan akses hardware. Bungkus GPIO, susun Node dengan composition, uji di PC dengan stub, lalu flash ke ESP32.
Artikel ini adalah #51 (ini) — jembatan Tier 2 setelah Factory & Strategy (#50), mengarah ke Seri IoT. Lanjut web: Flask / FastAPI (#52).
Tier 2 progress: langkah #51 (ini) · Factory (#50) LIVE · Flask/FastAPI (#52) · Seri 3 tetap 10/10. Prasyarat: Factory (#50) · Composition (#47) · Capstone (#49) · OOP (#40) · Greenhouse (#39) · ESP32 (#1).