Upload files to "/"
This commit is contained in:
parent
23cc472175
commit
8849616635
File diff suppressed because one or more lines are too long
|
|
@ -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
|
||||||
|
|
@ -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'
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiClientSecure.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <DHT.h>
|
||||||
|
#include <time.h>
|
||||||
|
// ================= 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue