From 8849616635806d57ae03f8ff6d640c8c8f2b1438 Mon Sep 17 00:00:00 2001 From: Maulanaiqbalmuhlisin210405 Date: Thu, 30 Jul 2026 13:56:18 +0700 Subject: [PATCH] Upload files to "/" --- .flutter-plugins-dependencies | 1 + .gitignore | 45 ++++++ .metadata | 45 ++++++ analysis_options.yaml | 28 ++++ iot.cpp | 276 ++++++++++++++++++++++++++++++++++ 5 files changed, 395 insertions(+) create mode 100644 .flutter-plugins-dependencies create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 analysis_options.yaml create mode 100644 iot.cpp diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies new file mode 100644 index 0000000..79e1299 --- /dev/null +++ b/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_links","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links-7.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_ios-6.4.1\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"app_links","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links-7.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.23\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.23\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_android-6.3.29\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"app_links","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links-7.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.6.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_macos-3.2.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"app_links_linux","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links_linux-1.0.3\\\\","native_build":false,"dependencies":["gtk"],"dev_dependency":false},{"name":"gtk","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\gtk-2.1.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.2.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"app_links","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links-7.0.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"app_links_web","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\app_links_web-1.0.4\\\\","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"C:\\\\Users\\\\USER\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.4.2\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"app_links","dependencies":["app_links_linux","app_links_web"]},{"name":"app_links_linux","dependencies":["gtk"]},{"name":"app_links_web","dependencies":[]},{"name":"gtk","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2026-04-07 22:13:08.415409","version":"3.41.6","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f0d006 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..d9584b6 --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "db50e20168db8fee486b9abf32fc912de3bc5b6a" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: android + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: ios + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: linux + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: macos + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: web + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: windows + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..d4e0f0c --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/iot.cpp b/iot.cpp new file mode 100644 index 0000000..5fed786 --- /dev/null +++ b/iot.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include +// ================= SUPABASE ================= +const char* supabase_url = "https://hpmicdhjyboyeofphgae.supabase.co"; +const char* supabase_key = "sb_publishable_OB8e9y1OO0z3Y5xQ828YvA_T3jD16zT"; + +// ================= WIFI ================= +const char* ssid = "Ozie 1"; +const char* password = "followkami"; + +// ================= TIME (NTP) ================= +const char* ntpServer = "pool.ntp.org"; +const long gmtOffset_sec = 7 * 3600; // WIB (GMT+7) +const int daylightOffset_sec = 0; + +// ================= MQTT ================= +const char* mqtt_server = "broker.hivemq.com"; + +WiFiClient espClient; +PubSubClient client(espClient); + +// ================= DHT ================= +#define DHTPIN 4 +#define DHTTYPE DHT22 +DHT dht(DHTPIN, DHTTYPE); + +// ================= RELAY ================= +#define RELAY1 27 +#define RELAY2 26 + +// Relay aktif LOW +#define RELAY_ON LOW +#define RELAY_OFF HIGH + +// ================= THRESHOLD ================= +float batasPanas = 70.0; +float batasLembab = 90.0; + +// ================= GLOBAL STATE ================= +unsigned long lastMsg = 0; +String statusRelay = "INIT"; +bool isAutoMode = true; +bool manualPump = false; +bool manualLight = false; + +// State Tracking for Upload +bool prevPump = false; +bool prevLight = false; +unsigned long lastPeriodicUpload = 0; + +// ================= SUPABASE UPLOAD ================= +void uploadToSupabase(float hum, String status, String lamp, String pump) { + if (WiFi.status() == WL_CONNECTED) { + // Ambil waktu saat ini + struct tm timeinfo; + String timeStr = ""; + if (getLocalTime(&timeinfo)) { + char timeStringBuff[50]; + strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S", &timeinfo); + timeStr = String(timeStringBuff); + } + + WiFiClientSecure clientSecure; + clientSecure.setInsecure(); + HTTPClient http; + + String url = String(supabase_url) + "/rest/v1/humidity"; + http.begin(clientSecure, url); + http.addHeader("Content-Type", "application/json"); + http.addHeader("apikey", supabase_key); + http.addHeader("Authorization", "Bearer " + String(supabase_key)); + http.addHeader("Prefer", "return=minimal"); + + String json = "{\"kelembapan\":" + String(hum) + + (timeStr != "" ? ",\"tanggal_upload\":\"" + timeStr + "\"" : "") + + ",\"status\":\"" + status + "\"" + + ",\"status_lampu\":\"" + lamp + "\"" + + ",\"status_pompa\":\"" + pump + "\"}"; + + Serial.println("[Supabase] Uploading data..."); + int httpCode = http.POST(json); + + if (httpCode >= 200 && httpCode < 300) { + Serial.printf("[Supabase] Berhasil Terkirim! Status: %d\n", httpCode); + } else if (httpCode > 0) { + String response = http.getString(); + Serial.printf("[Supabase] Gagal Mengirim. HTTP Code: %d\n", httpCode); + Serial.printf("[Supabase] Alasan: %s\n", response.c_str()); + } else { + Serial.printf("[Supabase] Gagal Koneksi. Error: %s\n", http.errorToString(httpCode).c_str()); + } + + http.end(); + } else { + Serial.println("[Supabase] Gagal: WiFi tidak terhubung!"); + } +} + +// ================= WIFI ================= +void setup_wifi() { + delay(10); + Serial.println("Connecting to WiFi..."); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("\nWiFi connected!"); +} + +// ================= MQTT CALLBACK ================= +void callback(char* topic, byte* payload, unsigned int length) { + String message = ""; + for (int i = 0; i < length; i++) { + message += (char)payload[i]; + } + + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + Serial.println(message); + + if (String(topic) == "esp32/statusiqbal/mode") { + isAutoMode = (message == "auto"); + Serial.println(isAutoMode ? "Mode: AUTO" : "Mode: MANUAL"); + } + else if (String(topic) == "esp32/statusiqbal/pump") { + manualPump = (message == "on"); + Serial.println(manualPump ? "Manual Pump: ON" : "Manual Pump: OFF"); + } + else if (String(topic) == "esp32/statusiqbal/light") { + manualLight = (message == "on"); + Serial.println(manualLight ? "Manual Light: ON" : "Manual Light: OFF"); + } +} + +// ================= MQTT RECONNECT ================= +void reconnect() { + while (!client.connected()) { + Serial.print("Connecting MQTT..."); + + // Create a unique client ID using MAC address + String clientId = "ESP32MJ-" + WiFi.macAddress(); + + if (client.connect(clientId.c_str(), "esp32/statusiqbal", 0, true, "offline")) { + Serial.println("CONNECTED to MQTT ✅"); + client.publish("esp32/statusiqbal", "online", true); + + // Subscribe to control topics + client.subscribe("esp32/statusiqbal/mode"); + client.subscribe("esp32/statusiqbal/pump"); + client.subscribe("esp32/statusiqbal/light"); + } else { + Serial.print("FAILED, rc="); + Serial.print(client.state()); + Serial.println(" trying again in 5s..."); + delay(5000); + } + } +} + +void setup() { + Serial.begin(115200); + + pinMode(RELAY1, OUTPUT); + pinMode(RELAY2, OUTPUT); + + digitalWrite(RELAY1, RELAY_OFF); + digitalWrite(RELAY2, RELAY_OFF); + + dht.begin(); + + setup_wifi(); + + // Konfigurasi waktu dari NTP + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + if (!client.connected()) { + reconnect(); + } + client.loop(); + + unsigned long now = millis(); + + if (now - lastMsg > 2000) { + lastMsg = now; + + float suhu = dht.readTemperature(); + float kelembapan = dht.readHumidity(); + + if (isnan(suhu) || isnan(kelembapan)) { + Serial.println("Gagal baca DHT!"); + return; + } + + // ================= CONTROL LOGIC ================= + if (isAutoMode) { + // Logic: <70% Pump ON, Lamp OFF | >90% Lamp ON, Pump OFF | 70-90% NORMAL (OFF/OFF) + if (kelembapan < 70.0) { + digitalWrite(RELAY1, RELAY_ON); // Pompa Hidup + digitalWrite(RELAY2, RELAY_OFF); // Lampu Mati + statusRelay = "KERING / PENYIRAMAN"; + } + else if (kelembapan > 90.0) { + digitalWrite(RELAY1, RELAY_OFF); // Pompa Mati + digitalWrite(RELAY2, RELAY_ON); // Lampu Nyala + statusRelay = "TERLALU LEMBAB"; + } + else { + digitalWrite(RELAY1, RELAY_OFF); // Pompa Mati + digitalWrite(RELAY2, RELAY_OFF); // Lampu Mati + statusRelay = "NORMAL"; + } + } else { + // Manual Mode (App Based) + digitalWrite(RELAY1, manualPump ? RELAY_ON : RELAY_OFF); + digitalWrite(RELAY2, manualLight ? RELAY_ON : RELAY_OFF); + statusRelay = "MANUAL"; + } + + // ================= STATUS REPORTING ================= + bool currentPump = (digitalRead(RELAY1) == RELAY_ON); + bool currentLight = (digitalRead(RELAY2) == RELAY_ON); + + // Upload logic: Action transition OR Periodic (30 mins) if Normal + unsigned long nowMillis = millis(); + bool actionDetected = (currentPump != prevPump || currentLight != prevLight); + + // Interval 30 menit = 1800000 ms + bool isPeriodicTime = (nowMillis - lastPeriodicUpload >= 1800000); + + if (actionDetected) { + Serial.println("[Reporting] Deteksi Perubahan Status!"); + } + + if (actionDetected || (statusRelay == "NORMAL" && isPeriodicTime)) { + String statusLampuUpload = currentLight ? (isAutoMode ? "MENYALA - OTOMATIS" : "MENYALA - MANUAL") : "MATI"; + String statusPompaUpload = currentPump ? (isAutoMode ? "MENYALA - OTOMATIS" : "MENYALA - MANUAL") : "MATI"; + + Serial.println("[Reporting] Memulai Upload ke Supabase..."); + uploadToSupabase(kelembapan, statusRelay, statusLampuUpload, statusPompaUpload); + lastPeriodicUpload = nowMillis; + } + + // MQTT Reporting + client.publish("esp32/dht/suhu", String(suhu).c_str()); + client.publish("esp32/dht/kelembapan", String(kelembapan).c_str()); + client.publish("esp32/relay/status", statusRelay.c_str()); + client.publish("esp32/statusiqbal/pump", currentPump ? "on" : "off", true); + client.publish("esp32/statusiqbal/light", currentLight ? "on" : "off", true); + client.publish("esp32/statusiqbal/mode", isAutoMode ? "auto" : "manual", true); + + // Save states for next comparison + prevPump = currentPump; + prevLight = currentLight; + + // Serial Debug + Serial.print("Mode: "); Serial.print(isAutoMode ? "AUTO" : "MANUAL"); + Serial.print(" | Hum: "); Serial.print(kelembapan); + Serial.print("% | Pump: "); Serial.print(currentPump ? "ON" : "OFF"); + Serial.print(" | Light: "); Serial.println(currentLight ? "ON" : "OFF"); + Serial.print(" | Status: "); Serial.println(statusRelay); + } +} \ No newline at end of file