Kebun Pintar Sekolah

15 tahun ke atas2 hari × 4.5 jamESP32GeminiIoT · MQTT
Dalam dua hari, kamu akan belajar menjadi jurutera yang mengarah AI: kamu tentukan perkakasan, keperluan dan ujian. Gemini menulis kod. Kamu menyemak, menguji dan bertanggungjawab ke atas hasilnya.
Tekan Seterusnya › atau kekunci anak panah untuk bertukar halaman. Perkataan bergaris titik seperti sistem terbenam boleh ditekan untuk melihat maksudnya.
Dua hari, satu kebun
| Hari | Bahagian | Soalan yang dijawab | Masa |
|---|---|---|---|
| 1 | 1 Sistem terbenam | Apakah ESP32 dan bagaimana ia berfikir? | 25 min |
| 1 | 2 Elektrik asas | Berapa ohm perintang untuk LED, dan kenapa? | 40 min |
| 1 | 3 Digital, analog, PWM | Bagaimana membaca sensor dan memalapkan lampu? | 45 min |
| 1 | 4 Sensor & aktuator | Apa yang kebun perlukan, dan di pin mana? | 40 min |
| 1 | 5 Kejuruteraan prompt | Bagaimana menulis spesifikasi yang Gemini tidak boleh salah faham? | 60 min |
| 1 | 6 Aliran kerja Makmal | Bagaimana membina, menjalankan dan membaca ralat? | 40 min |
| 2 | 7 Menyemak kod AI | Kod berjalan, tetapi betulkah ia? | 45 min |
| 2 | 8 Nyahpepijat | Bagaimana mencari punca masalah dengan sistematik? | 30 min |
| 2 | 9 IoT: Wi-Fi, HTTP, MQTT | Bagaimana kebun melapor ke telefon? | 60 min |
| 2 | 10 AI secara bertanggungjawab | Apa tanggungjawab kita apabila AI menulis kod? | 15 min |
| 2 | 11 Projek akhir | Bolehkah pasukan kamu membina Kebun Pintar? | 2 jam |
Masa ialah anggaran tanpa rehat. Cikgu boleh mengubah suai mengikut kelas.
Komputer yang tersembunyi di mana-mana
Telefon kamu ada pemproses berbilang teras. Tetapi pengawal penyaman udara, mesin basuh dan lampu isyarat hanya perlukan komputer kecil yang murah dan jimat tenaga: mikropengawal.
Sensor mengukur dunia: suhu, kelembapan tanah, cahaya, butang.
Kod membuat keputusan: “jika tanah kering, siram”.
Aktuator bertindak: pam air, LED, paparan LCD, mesej Wi-Fi.
| Arduino Uno | ESP32 DevKit | |
|---|---|---|
| Pemproses | 8-bit, 16 MHz | 32-bit dwi-teras, 240 MHz |
| Voltan logik | 5 V | 3.3 V |
| Analog (ADC) | 10-bit (0 hingga 1023) | 12-bit (0 hingga 4095) |
| Wi-Fi / Bluetooth | Tiada | Ada |
Untuk kebun yang melapor ke telefon, kita pilih ESP32.
Bagaimana program Arduino berjalan
Setiap program Arduino (dipanggil sketch) ada dua bahagian wajib:
void setup() { // berjalan SEKALI semasa papan dihidupkan // contoh: tetapkan pin, mulakan Serial, sambung Wi-Fi } void loop() { // berjalan BERULANG-ULANG selama-lamanya, beribu kali sesaat // contoh: baca sensor, buat keputusan, kawal pam }
Kamu tidak perlu menulis kod ini sendiri hari ini, tetapi kamu mesti boleh membacanya untuk menyemak kerja Gemini.
Voltan, arus dan rintangan

Bayangkan paip air di kebun:
- Voltan (V) ialah tekanan air, iaitu daya yang menolak.
- Arus (I) ialah banyaknya air yang mengalir setiap saat.
- Rintangan (R) ialah paip yang sempit, yang menghadkan aliran.
Ketiga-tiganya dihubungkan oleh Hukum Ohm:
Mengira perintang untuk LED
LED “memakan” sebahagian voltan, dipanggil voltan hadapan (Vf). Baki voltan jatuh pada perintang:
Contoh: ESP32 (3.3 V), LED merah (2.0 V), arus 10 mA = 0.010 A:
R = 3.3 − 2.00.010 = 1.30.010 = 130 Ω
Perintang dijual dalam nilai piawai. Kita pilih nilai piawai yang sama atau lebih besar: 150 Ω atau 220 Ω (arus lebih kecil, lebih selamat, LED masih cukup terang).
Kira perintang untuk setiap kes. Tunjukkan jalan kira sebagai pecahan.
| Bekalan | LED | Arus | R dikira | Nilai piawai dipilih |
|---|---|---|---|---|
| 5 V | Merah (2.0 V) | 15 mA | ||
| 3.3 V | Hijau (2.2 V) | 5 mA | ||
| 5 V | Biru (3.0 V) | 10 mA |
Isyarat digital dan perintang tarik-naik
Isyarat digital hanya ada dua keadaan: HIGH (3.3 V) atau LOW (0 V). Butang ialah input digital.
Pin input yang tidak disambung ke mana-mana akan “terapung” dan membaca nilai rawak. Penyelesaiannya ialah perintang tarik-naik. ESP32 ada satu di dalamnya:
pinMode(BUTANG, INPUT_PULLUP); // butang antara pin dan GND if (digitalRead(BUTANG) == LOW) { /* ditekan */ }
Isyarat analog: membaca nilai di antara
Sensor kelembapan tanah dan sensor cahaya memberi voltan di antara 0 dan 3.3 V. ADC menukar voltan itu kepada nombor:
Gerakkan peluncur untuk menukar voltan sensor.
Vmasuk = 1.65 V → analogRead() = 2048
PWM: “analog palsu” untuk memalapkan

Pin digital hanya boleh HIGH atau LOW. Untuk memalapkan LED atau mengawal kelajuan kipas, kita tukar HIGH dan LOW dengan sangat laju. Peratus masa HIGH dipanggil kitar tugas (duty cycle).
Kitar tugas 25% → nilai ledcWrite(..., 64)
Dalam ESP32 core 2.0.17: ledcSetup(saluran, frekuensi, resolusi) dan ledcAttachPin(pin, saluran), kemudian ledcWrite(saluran, nilai). Nyatakan versi core dalam prompt supaya Gemini guna fungsi yang betul.
Komponen Kebun Pintar

| Komponen | Jenis | Sambungan | Dalam Wokwi |
|---|---|---|---|
| 🌡️ DHT22 suhu & kelembapan udara | INPUT digital | VCC 3V3, GND, SDA → GPIO 15 | wokwi-dht22 |
| 💧 Sensor kelembapan tanah | INPUT analog | AO → GPIO 34 | Ganti dengan potensiometer (Wokwi tiada sensor tanah) |
| ☀️ Modul LDR (cahaya) | INPUT analog | AO → GPIO 35 | wokwi-photoresistor-sensor |
| 🔘 Butang siram manual | INPUT digital | GPIO 4 ↔ GND, INPUT_PULLUP | wokwi-pushbutton |
| ⚡ Modul geganti (pam) | OUTPUT | IN → GPIO 26 | wokwi-relay-module |
| 📟 LCD 16×2 I2C | OUTPUT I2C | SDA → GPIO 21, SCL → GPIO 22, alamat 0x27 | wokwi-lcd1602 (pins: i2c) |
| 🔔 Buzzer amaran | OUTPUT | + → GPIO 25 | wokwi-buzzer |
Geganti dan I2C
⚡ Geganti (relay)
Suis yang dikawal elektrik. Pin ESP32 yang lemah menghidupkan suis, dan suis itu menyambungkan bekalan pam yang lebih kuat.
Keselamatan: dalam bengkel, kita hanya guna pam kecil 5 V hingga 12 V. Jangan sekali-kali menyambung bekalan elektrik rumah (240 V) tanpa pengawasan juruteknik bertauliah.
📟 I2C: dua wayar, banyak peranti
I2C menggunakan dua wayar sahaja: SDA (data) dan SCL (jam). Banyak peranti boleh berkongsi dua wayar ini kerana setiap satu ada alamat (contohnya LCD = 0x27).
Pada ESP32: SDA = GPIO 21, SCL = GPIO 22. Jangan guna pin ini untuk benda lain.

Aktiviti: Pelan Pin kebun
Jurutera tidak menyambung wayar secara rawak. Mereka menulis Pelan Pin dahulu, iaitu satu jadual yang menjadi kontrak antara litar, prompt dan kod.
Pilih komponen dan pin. Halaman ini menyemak pin bertindih, pin yang dilarang, dan pin ADC2 yang tidak boleh digunakan bersama Wi-Fi. Pelan disimpan untuk Pembina Spesifikasi.
| Guna? | Komponen | Pin |
|---|
| Komponen | Pin | Jenis | Nota (voltan, alamat, ADC1?) |
|---|---|---|---|
| DHT22 | |||
| Sensor tanah (potensiometer) | |||
| LDR | |||
| Butang siram | |||
| Geganti pam | |||
| LCD I2C (SDA/SCL) | |||
| Buzzer |
Litar siap untuk Wokwi: kebun-diagram.json (salin ke diagram.json projek).
Prompt ialah spesifikasi

Gemini menulis kod berdasarkan apa yang kamu tulis sahaja. Ia tidak nampak litar kamu, tidak tahu versi pustaka kamu, dan akan meneka apa yang tidak dinyatakan. Setiap tekaan ialah peluang untuk pepijat.
Jurutera menulis spesifikasi. Prompt yang baik ialah spesifikasi yang ringkas, dengan lima bahagian:
Menulis keperluan yang boleh diuji
Keperluan yang baik boleh dijawab dengan lulus atau gagal. Elakkan perkataan kabur seperti “cepat”, “kering”, “kadang-kadang”.
| Kabur ❌ | Boleh diuji ✅ |
|---|---|
| Siram bila tanah kering. | K1. Jika bacaan tanah (GPIO 34) > 3000, hidupkan geganti (GPIO 26) selama 5 saat, kemudian tunggu sekurang-kurangnya 60 saat sebelum menyiram semula. |
| Tunjuk suhu. | K2. Setiap 2 saat, papar “S:28.5C L:65%” pada baris 1 LCD (1 titik perpuluhan). |
| Beri amaran bila panas. | K3. Jika suhu ≥ 35.0 °C, buzzer berbunyi 2000 Hz 200 ms setiap 5 saat sehingga suhu < 34.0 °C. |
Perhatikan K3 menggunakan dua nilai (35.0 dan 34.0). Ini dipanggil histeresis, iaitu ia mengelakkan buzzer berbunyi-berhenti tanpa henti apabila suhu berada di sekitar 35 °C.
Kekangan: menghalang tabiat buruk AI
Tanpa kekangan, kod AI selalunya “berjalan” tetapi rapuh. Kekangan yang berguna untuk projek kita:
- Versi: “ESP32 Arduino core 2.0.17. Guna ledcSetup/ledcAttachPin untuk PWM.”
- Pustaka: “Guna hanya pustaka ini: DHT sensor library, LiquidCrystal_I2C, PubSubClient, ArduinoJson v7.”
- Tidak menyekat: “Jangan guna delay() lebih 50 ms dalam loop(). Guna millis() untuk pemasaan.”
- Kebolehbacaan: “Semua pin dan ambang sebagai pemalar bernama di bahagian atas.”
- Ketahanan: “Semak jika bacaan DHT ialah NaN dan jangan guna bacaan itu.”
- Format jawapan: “Beri kod penuh dalam satu blok. Selepas kod, terangkan dalam 5 baris bagaimana ia memenuhi K1 hingga K3.”
delay(5000) membekukan papan selama 5 saat: butang tidak dibaca, LCD tidak dikemas kini, MQTT terputus. Dengan millis(), program menyemak jam dan terus bekerja.Teknik prompt yang digunakan jurutera
| Teknik | Contoh ayat | Bila digunakan |
|---|---|---|
| Rancang dahulu | “Sebelum menulis kod, senaraikan langkah dan fungsi yang akan kamu buat. Tunggu saya setuju.” | Projek besar |
| Minta soalan | “Jika ada apa-apa yang tidak jelas dalam spesifikasi, tanya saya dahulu.” | Sebelum kod pertama |
| Satu perubahan | “Kod sedia ada berfungsi. Tambah K4 sahaja. Jangan ubah bahagian lain.” | Menambah ciri |
| Ralat + kod | “Ini ralat compile dan kod penuh. Terangkan punca, kemudian beri kod yang dibetulkan.” | Ralat compile |
| Dijangka vs berlaku | “Dijangka: pam hidup 5 s. Berlaku: pam hidup terus. Output Serial: …” | Pepijat logik |
| Terangkan | “Terangkan baris 40 hingga 55 seolah-olah kepada pelajar Tingkatan 4.” | Memahami kod |
Aktiviti: Pembina Spesifikasi
Pelan Pin dari Bahagian 4 dimasukkan automatik. Tulis keperluan (satu baris setiap satu), pilih kekangan, tulis ujian, kemudian salin ke Gemini.
3 · Keperluan (satu setiap baris)
4 · Kekangan
5 · Ujian penerimaan
Prompt kamu:
Aliran kerja Makmal Arduino Maya
- Buka Makmal ESP32 dari Desktop. Untuk projek baharu, tekan 📁 Projek Baharu.
- Litar: lukis di wokwi.com (jangan tekan Play di sana), salin tab
diagram.jsonkediagram.jsonprojek. Atau guna fail litar yang cikgu beri. - Kod: tampal kod Gemini ke dalam
sketch.ino(Ctrl+A, Ctrl+V). - ▶ Bina & Jalankan (F5). Simulator terbuka. Output Serial muncul di Terminal.
- Ralat? Tekan 📋 Salin untuk Gemini, kemudian tampal dalam Gemini. Ralat dan kod disalin bersama.
Dalam simulator, klik pada DHT22 atau potensiometer untuk menukar suhu dan kelembapan tanah semasa program berjalan. Ini cara terbaik untuk menguji K1 hingga K3.
Membaca ralat compile
Mesej ralat bermula dengan sketch.ino:baris:lajur: error:. Baca baris pertama dahulu, kerana ralat lain selalunya akibat yang pertama.
| Mesej | Punca biasa | Tindakan |
|---|---|---|
'X' was not declared in this scope | Nama belum ditakrif, atau salah ejaan | Semak ejaan; minta Gemini menambah definisi |
No such file or directory | Pustaka tiada dalam Makmal | Minta Gemini guna pustaka yang disenaraikan |
'ledcAttach' was not declared | Kod untuk core 3.x | “Guna core 2.0.17 (ledcSetup/ledcAttachPin)” |
expected ';' before | Titik koma hilang pada baris sebelumnya | Lihat baris di atas nombor yang diberi |
redefinition of 'void setup()' | Kod ditampal dua kali | Ctrl+A, padam, tampal sekali sahaja |
Kod berjalan ≠ kod betul

AI boleh menghasilkan kod yang kelihatan meyakinkan tetapi mengandungi kesilapan halus. Sebelum menjalankan kod pada perkakasan sebenar (contohnya pam), lakukan semakan kod:
- Pin: setiap pemalar pin sama dengan Pelan Pin?
- Keperluan: setiap K1, K2, K3 ada dalam kod? Cari nilai ambang dan masa.
- Pemasaan: ada
delay()panjang yang membekukan program? - Keadaan selamat: apa berlaku jika sensor gagal (NaN)? Adakah pam boleh hidup tanpa henti?
- Nombor ajaib: nilai seperti 3000 atau 5000 ditulis sebagai pemalar bernama?
- Faham: bolehkah kamu terangkan setiap fungsi kepada rakan?
Aktiviti: Cari pepijat
Gemini memberi kod ini untuk K1 (“jika tanah > 3000, pam 5 saat, kemudian rehat 60 saat”). Pelan Pin: tanah GPIO 34, geganti GPIO 26.
const int TANAH = 34; const int PAM = 27; const int AMBANG = 3000; void loop() { int t = analogRead(TANAH); if (t > AMBANG) { digitalWrite(PAM, HIGH); delay(5000); digitalWrite(PAM, LOW); } }
Corak millis() yang patut kamu kenal
Kamu tidak perlu menulisnya, tetapi kenali bentuknya semasa menyemak kod Gemini:
unsigned long mulaPam = 0; bool pamHidup = false; void loop() { unsigned long kini = millis(); if (!pamHidup && tanahKering() && kini - akhirSiram >= REHAT_MS) { pamHidup = true; mulaPam = kini; digitalWrite(PAM, HIGH); } if (pamHidup && kini - mulaPam >= SIRAM_MS) { pamHidup = false; akhirSiram = kini; digitalWrite(PAM, LOW); } // ...bahagian lain terus berjalan: LCD, butang, MQTT }
Tanda-tanda kod yang baik: kini - mula >= TEMPOH (selamat walaupun millis() melimpah selepas 49 hari), dan pemboleh ubah keadaan seperti pamHidup. Ini dipanggil mesin keadaan.
Tampal kod Gemini. Pemeriksa membandingkan pin dalam kod dengan Pelan Pin kamu (Bahagian 4).
| Semakan | Lulus? | Catatan |
|---|---|---|
| Pin sama dengan Pelan Pin | ☐ | |
| Semua keperluan K1 hingga K3 ada | ☐ | |
| Tiada delay() panjang | ☐ | |
| Keadaan selamat (NaN, pam tidak hidup terus) | ☐ | |
| Pemalar bernama | ☐ | |
| Saya boleh terangkan kod | ☐ |
Nyahpepijat seperti saintis
Nyahpepijat ialah kaedah saintifik: buat hipotesis, uji, perhatikan.
1 · Lihat dengan Serial
Minta Gemini menambah Serial.printf() pada setiap keputusan penting: nilai sensor, keadaan pam, masa.
2 · Bahagi dan takluk
Uji satu komponen pada satu masa. LCD tidak papar? Cuba program LCD sahaja dahulu.
3 · Ubah satu benda
Hanya satu perubahan antara setiap ujian, supaya kamu tahu perubahan mana yang membetulkan.
4 · Litar dahulu
Semak diagram.json: wayar ke pin betul? GND disambung? LCD guna alamat 0x27?
Internet of Things

IoT bermaksud benda fizikal yang bercakap melalui Internet. Kebun kita akan menghantar data ke awan, dan sesiapa yang dibenarkan boleh melihatnya.
Dalam simulator, ESP32 bersambung ke rangkaian Wokwi-GUEST (tiada kata laluan):
WiFi.begin("Wokwi-GUEST", "", 6); // saluran 6 = sambungan lebih cepat
while (WiFi.status() != WL_CONNECTED) { delay(200); }
Dua cara bercakap: HTTP dan MQTT
HTTP
Seperti menghantar surat dan menunggu balasan. ESP32 membuat permintaan (GET atau POST) ke pelayan web, dan pelayan membalas dengan kod (200 = OK).
Sesuai untuk laporan sekali-sekala.
MQTT
Seperti kumpulan WhatsApp. Peranti menerbitkan (publish) mesej ke topik di broker, dan sesiapa yang melanggan (subscribe) topik itu menerimanya serta-merta.
Ringan, sesuai untuk IoT dan data berterusan.
Data dihantar dalam format JSON:
{"suhu": 28.5, "lembap": 65, "tanah": 3120, "pam": false}
Merancang topik MQTT
Kita guna broker awam percuma broker.hivemq.com (port 1883). Kerana ia awam, sesiapa sahaja boleh membaca topik yang diteka. Oleh itu, guna topik yang unik dan jangan hantar data peribadi.
Struktur topik bengkel: makmal/<kod sekolah>/<kumpulan>/<data>
Untuk melihat data: buka klien web MQTT (contohnya HiveMQ WebSocket Client), sambung ke broker.hivemq.com, dan langgan makmal/smk01/k3/#.
Kamu jurutera, AI pembantu

- Kamu bertanggungjawab ke atas kod yang kamu gunakan, walaupun AI yang menulisnya.
- Faham sebelum guna: jangan hantar kod yang kamu tidak boleh terangkan.
- Jujur: nyatakan “dibina dengan bantuan Gemini” dalam laporan dan pembentangan.
- Privasi: jangan masukkan data peribadi, kata laluan atau maklumat sekolah yang sulit ke dalam prompt.
- Sahkan: AI boleh salah dengan yakin. Uji setiap keperluan.
- Keselamatan fizikal: kod yang mengawal pam, motor atau pemanas mesti ada had masa dan keadaan selamat.
Projek akhir: Kebun Pintar Sekolah

Taklimat: bina sistem yang menjaga kebun sekolah semasa hujung minggu dan melapor keadaannya. Pasukan 3 hingga 4 orang, 2 jam.
| Tahap | Keperluan |
|---|---|
| Minimum | DHT22 dan sensor tanah dibaca; LCD memaparkan suhu, kelembapan dan tanah; pam (geganti) disiram mengikut K1 dengan rehat. |
| Sasaran | + Butang siram manual dengan nyahlantun; amaran suhu dengan histeresis; tiada delay() panjang. |
| Cabaran | + Terbit JSON ke MQTT setiap 10 s; terima arahan “siram” melalui topik MQTT; LDR untuk tidak menyiram pada tengah hari terik. |
Peranan pasukan: Jurutera Perkakasan (litar, Pelan Pin) · Jurutera Spesifikasi (prompt, keperluan) · Penguji (ujian, semakan kod) · Pembentang (demo, log).
Hasil kerja dan rubrik
- Pelan Pin dan
diagram.json. - Log prompt: setiap prompt yang digunakan, dan apa yang berubah selepasnya.
- Kod akhir dengan komen.
- Jadual ujian: setiap keperluan, langkah ujian, lulus/gagal.
- Demo 3 minit dalam simulator, termasuk satu keadaan gagal yang dikendalikan dengan selamat.
| Kriteria | Cemerlang (4) | Baik (3) | Asas (2) |
|---|---|---|---|
| Fungsi | Semua keperluan sasaran + satu cabaran | Semua keperluan sasaran | Keperluan minimum |
| Spesifikasi & prompt | Keperluan bernombor, kekangan dan ujian jelas; log lengkap | Kebanyakan keperluan boleh diuji | Prompt umum |
| Kualiti & keselamatan kod | millis(), pemalar bernama, NaN, had pam | Sebahagian ciri | Berjalan tetapi rapuh |
| Ujian | Setiap keperluan diuji dan direkod | Kebanyakan diuji | Ujian tidak direkod |
| Pembentangan | Jelas, menerangkan kod dan peranan AI | Jelas | Kurang jelas |
Jadual ujian pasukan
| Kep. | Langkah ujian dalam simulator | Dijangka | Lulus? |
|---|---|---|---|
| K1 | Pusing potensiometer tanah melebihi 3000 | “Pam ON”, geganti hidup 5 s, kemudian rehat 60 s | ☐ |
| K2 | ☐ | ||
| K3 | ☐ | ||
| K4 | ☐ | ||
| K5 | ☐ |
Apa yang kamu bawa pulang
- Hukum Ohm untuk mengira perintang, dan had arus pin ESP32.
- Digital, analog (ADC 12-bit, guna ADC1 dengan Wi-Fi) dan PWM.
- Pelan Pin sebagai kontrak antara litar, prompt dan kod.
- Prompt sebagai spesifikasi: konteks, perkakasan, keperluan, kekangan, ujian.
- Semak kod AI: pin, keperluan, millis(), keadaan selamat, kefahaman.
- IoT dengan Wi-Fi, HTTP, JSON dan MQTT, dengan privasi dalam fikiran.
- Kamu jurutera, AI pembantu. Tanggungjawab tetap pada kamu.
Seterusnya: cuba bina kebun ini dengan perkakasan sebenar. Kod yang sama boleh dimuat naik ke ESP32 sebenar.
Jadual dan persediaan
| Hari 1 | Hari 2 | ||
|---|---|---|---|
| 08:30 | Bahagian 1–2 | 08:30 | Ulang kaji + Bahagian 7 |
| 09:35 | Bahagian 3 | 09:20 | Bahagian 8 |
| 10:20 | Rehat | 09:50 | Bahagian 9 |
| 10:40 | Bahagian 4 | 10:50 | Rehat |
| 11:20 | Bahagian 5 | 11:10 | Bahagian 10 + taklimat projek |
| 12:20 | Bahagian 6 + Makmal 1 (PWM + butang) | 11:30 | Projek akhir |
| 13:00 | Makmal 2 (DHT22 → LCD), tamat | 13:30 | Demo dan penutup |
- Pasang Makmal Arduino Maya dan aktifkan lesen Wokwi pada setiap PC. Uji satu projek dengan Wi-Fi.
- Salin
litar/kebun-diagram.jsonke setiap PC atau kongsi melalui Google Classroom. Uji ia dalam Makmal sebelum kelas. - Semak broker MQTT boleh dicapai dari makmal (port 1883 keluar). Jika disekat, guna HTTP sahaja.
- Pastikan akaun Gemini pelajar (DELIMa) aktif.
Kod rujukan: Kebun Pintar (tahap sasaran + MQTT)
Sudah diuji dengan pengkompil Makmal Arduino Maya (ESP32 core 2.0.17). Gunakan untuk menilai kod pasukan, bukan untuk diedarkan sebelum projek.
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
const int PIN_DHT = 15, PIN_TANAH = 34, PIN_LDR = 35;
const int PIN_BUTANG = 4, PIN_PAM = 26, PIN_BUZZER = 25;
const int AMBANG_TANAH = 3000; // lebih besar = lebih kering
const float SUHU_PANAS = 35.0, SUHU_OK = 34.0;
const unsigned long SIRAM_MS = 5000, REHAT_MS = 60000;
const unsigned long BACA_MS = 2000, TERBIT_MS = 10000;
const char* TOPIK = "makmal/smk01/k3/kebun";
DHT dht(PIN_DHT, DHT22);
LiquidCrystal_I2C lcd(0x27, 16, 2);
WiFiClient wifi;
PubSubClient mqtt(wifi);
float suhu = NAN, lembap = NAN;
int tanah = 0;
bool pamHidup = false, amaran = false;
unsigned long mulaPam = 0, akhirSiram = 0, akhirBaca = 0, akhirTerbit = 0, akhirBunyi = 0;
int butangLepas = HIGH; unsigned long masaButang = 0;
void mulaSiram(unsigned long kini) {
pamHidup = true; mulaPam = kini; digitalWrite(PIN_PAM, HIGH);
Serial.println("Pam ON");
}
void sambungMqtt() {
if (mqtt.connected()) return;
String id = "makmal-" + String((uint32_t)ESP.getEfuseMac(), HEX);
if (mqtt.connect(id.c_str())) Serial.println("MQTT bersambung");
}
void setup() {
Serial.begin(115200);
pinMode(PIN_BUTANG, INPUT_PULLUP);
pinMode(PIN_PAM, OUTPUT);
dht.begin();
lcd.init(); lcd.backlight();
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) { delay(200); Serial.print("."); }
mqtt.setServer("broker.hivemq.com", 1883);
Serial.println("\nKebun sedia");
}
void loop() {
unsigned long kini = millis();
sambungMqtt();
mqtt.loop();
if (kini - akhirBaca >= BACA_MS) { // K2: baca dan papar
akhirBaca = kini;
float s = dht.readTemperature(), l = dht.readHumidity();
if (!isnan(s) && !isnan(l)) { suhu = s; lembap = l; }
tanah = analogRead(PIN_TANAH);
lcd.setCursor(0, 0); lcd.printf("S:%4.1fC L:%3.0f%% ", suhu, lembap);
lcd.setCursor(0, 1); lcd.printf("Tanah:%4d %s ", tanah, pamHidup ? "PAM" : " ");
}
if (!pamHidup && tanah > AMBANG_TANAH && kini - akhirSiram >= REHAT_MS) mulaSiram(kini); // K1
int b = digitalRead(PIN_BUTANG); // siram manual, nyahlantun 50 ms
if (b != butangLepas && kini - masaButang > 50) {
masaButang = kini; butangLepas = b;
if (b == LOW && !pamHidup) mulaSiram(kini);
}
if (pamHidup && kini - mulaPam >= SIRAM_MS) {
pamHidup = false; akhirSiram = kini; digitalWrite(PIN_PAM, LOW);
Serial.println("Pam OFF");
}
if (!isnan(suhu)) { // K3: amaran dengan histeresis
if (suhu >= SUHU_PANAS) amaran = true;
if (suhu < SUHU_OK) amaran = false;
}
if (amaran && kini - akhirBunyi >= 5000) { akhirBunyi = kini; tone(PIN_BUZZER, 2000, 200); }
if (kini - akhirTerbit >= TERBIT_MS && mqtt.connected()) { // cabaran: MQTT
akhirTerbit = kini;
JsonDocument doc;
doc["suhu"] = suhu; doc["lembap"] = lembap; doc["tanah"] = tanah;
doc["cahaya"] = analogRead(PIN_LDR); doc["pam"] = pamHidup;
char buf[160]; serializeJson(doc, buf);
mqtt.publish(TOPIK, buf);
Serial.println(buf);
}
}