411 lines
14 KiB
C++
411 lines
14 KiB
C++
#define BLYNK_TEMPLATE_ID "TMPL66b-buG9w"
|
|
#define BLYNK_TEMPLATE_NAME "Monitoring Listrik"
|
|
#define BLYNK_AUTH_TOKEN "f3xiBla2UmCX2klP4VuzaYjXZlvyulJZ"
|
|
|
|
#include <Wire.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <PZEM004Tv30.h>
|
|
#include <WiFiManager.h>
|
|
#include <BlynkSimpleEsp32.h>
|
|
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <ArduinoJson.h>
|
|
#include <FS.h>
|
|
#include <ArduinoOTA.h>
|
|
#include <EEPROM.h>
|
|
|
|
// Define constants
|
|
#define BLYNK_TEMPLATE_ID_LEN 20
|
|
#define BLYNK_TEMPLATE_NAME_LEN 40
|
|
#define BLYNK_AUTH_TOKEN_LEN 40
|
|
#define MAX_LEN 40
|
|
|
|
char ssid[50];
|
|
char pass[50];
|
|
char lokasi_id[MAX_LEN];
|
|
char blynk_template_id[BLYNK_TEMPLATE_ID_LEN];
|
|
char blynk_template_name[BLYNK_TEMPLATE_NAME_LEN];
|
|
char blynk_auth_token[BLYNK_AUTH_TOKEN_LEN];
|
|
|
|
char created_at[50];
|
|
const char* ntpServer = "pool.ntp.org";
|
|
const long gmtOffset_sec = 25200;
|
|
const int daylightOffset_sec = 0;
|
|
|
|
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
|
PZEM004Tv30 pzem(Serial2, 16, 17);
|
|
float tarifPerKWh = 1352;
|
|
const int relayPin = 5;
|
|
const int buzzerPin = 15;
|
|
int relayState = LOW;
|
|
WiFiClient client;
|
|
IPAddress serverAddr;
|
|
|
|
unsigned long lastUploadTime = 0; // Variabel untuk menyimpan waktu upload terakhir
|
|
const unsigned long uploadInterval = 60000; // Interval 1 menit (60000 ms)
|
|
unsigned long lastBlynkUpdateTime = 0; // Variabel untuk menyimpan waktu update Blynk terakhir
|
|
const unsigned long blynkUpdateInterval = 1000; // Interval 1 detik (1000 ms)
|
|
unsigned long previousMillis = 0; // Waktu sebelumnya
|
|
const long buzzerDuration = 60000; // Durasi buzzer dalam milidetik (1 menit = 60 detik = 60000 milidetik)
|
|
|
|
void setup() {
|
|
WiFi.mode(WIFI_STA);
|
|
Serial.begin(115200);
|
|
EEPROM.begin(512);
|
|
pinMode(relayPin, OUTPUT);
|
|
pinMode(buzzerPin, OUTPUT);
|
|
|
|
// Membaca data dari EEPROM
|
|
String read_lokasi_id = EEPROM.readString(0);
|
|
String read_blynk_template_id = EEPROM.readString(20);
|
|
String read_blynk_template_name = EEPROM.readString(60);
|
|
String read_blynk_auth_token = EEPROM.readString(100);
|
|
|
|
// Menampilkan data yang dibaca di Serial Monitor
|
|
Serial.println("Reading from EEPROM:");
|
|
Serial.println("lokasi_id: " + read_lokasi_id);
|
|
Serial.println("blynk_template_id: " + read_blynk_template_id);
|
|
Serial.println("blynk_template_name: " + read_blynk_template_name);
|
|
Serial.println("blynk_auth_token: " + read_blynk_auth_token);
|
|
|
|
// Inisialisasi WiFiManager
|
|
WiFiManager wifiManager;
|
|
|
|
bool needConfiguration = false;
|
|
|
|
// Memeriksa apakah data EEPROM valid
|
|
if (read_lokasi_id.length() > 0 && read_blynk_template_id.length() > 0 &&
|
|
read_blynk_template_name.length() > 0 && read_blynk_auth_token.length() > 0) {
|
|
// Data valid, gunakan untuk konfigurasi Blynk
|
|
strncpy(lokasi_id, read_lokasi_id.c_str(), sizeof(lokasi_id));
|
|
strncpy(blynk_template_id, read_blynk_template_id.c_str(), sizeof(blynk_template_id));
|
|
strncpy(blynk_template_name, read_blynk_template_name.c_str(), sizeof(blynk_template_name));
|
|
strncpy(blynk_auth_token, read_blynk_auth_token.c_str(), sizeof(blynk_auth_token));
|
|
|
|
Serial.println("Using saved data from EEPROM:");
|
|
Serial.println("lokasi_id: " + String(lokasi_id));
|
|
Serial.println("blynk_template_id: " + String(blynk_template_id));
|
|
Serial.println("blynk_template_name: " + String(blynk_template_name));
|
|
Serial.println("blynk_auth_token: " + String(blynk_auth_token));
|
|
|
|
// Coba hubungkan ke WiFi dengan SSID yang disimpan
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Connecting to");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("WiFi.........");
|
|
delay(5000);
|
|
WiFi.begin(ssid, pass);
|
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
|
Serial.println("Failed to connect to WiFi with saved credentials. Opening configuration portal...");
|
|
needConfiguration = true;
|
|
} else {
|
|
Serial.println("Connected to WiFi with saved credentials");
|
|
|
|
// Periksa apakah SSID telah berubah
|
|
if (String(WiFi.SSID()) != String(ssid)) {
|
|
Serial.println("SSID has changed, opening configuration portal...");
|
|
needConfiguration = true;
|
|
}
|
|
}
|
|
} else {
|
|
// Data tidak valid atau kosong, buka portal konfigurasi
|
|
Serial.println("No saved data in EEPROM or data invalid. Opening configuration portal...");
|
|
needConfiguration = true;
|
|
}
|
|
|
|
if (needConfiguration) {
|
|
WiFiManagerParameter custom_lokasi_id("lokasi_id", "lokasi ID", lokasi_id, MAX_LEN);
|
|
WiFiManagerParameter custom_blynk_template_id("blynk_template_id", "Blynk Template ID", blynk_template_id, BLYNK_TEMPLATE_ID_LEN);
|
|
WiFiManagerParameter custom_blynk_template_name("blynk_template_name", "Blynk Template Name", blynk_template_name, BLYNK_TEMPLATE_NAME_LEN);
|
|
WiFiManagerParameter custom_blynk_auth_token("blynk_auth_token", "Blynk Auth Token", blynk_auth_token, BLYNK_AUTH_TOKEN_LEN);
|
|
|
|
wifiManager.addParameter(&custom_lokasi_id);
|
|
wifiManager.addParameter(&custom_blynk_template_id);
|
|
wifiManager.addParameter(&custom_blynk_template_name);
|
|
wifiManager.addParameter(&custom_blynk_auth_token);
|
|
|
|
if (!wifiManager.autoConnect("VoltTech", "password")) {
|
|
Serial.println("Failed to connect to WiFi and hit timeout");
|
|
ESP.restart();
|
|
delay(1000);
|
|
}
|
|
|
|
// Simpan parameter kustom ke variabel
|
|
strncpy(ssid, WiFi.SSID().c_str(), sizeof(ssid));
|
|
strncpy(pass, WiFi.psk().c_str(), sizeof(pass));
|
|
strncpy(lokasi_id, custom_lokasi_id.getValue(), sizeof(lokasi_id));
|
|
strncpy(blynk_template_id, custom_blynk_template_id.getValue(), sizeof(blynk_template_id));
|
|
strncpy(blynk_template_name, custom_blynk_template_name.getValue(), sizeof(blynk_template_name));
|
|
strncpy(blynk_auth_token, custom_blynk_auth_token.getValue(), sizeof(blynk_auth_token));
|
|
|
|
// Simpan parameter kustom ke EEPROM
|
|
EEPROM.writeString(0, lokasi_id);
|
|
EEPROM.writeString(20, blynk_template_id);
|
|
EEPROM.writeString(60, blynk_template_name);
|
|
EEPROM.writeString(100, blynk_auth_token);
|
|
EEPROM.commit();
|
|
|
|
// Tampilkan data yang ditulis ke EEPROM di Serial Monitor
|
|
Serial.println("Saved new data to EEPROM:");
|
|
Serial.println("lokasi_id: " + String(lokasi_id));
|
|
Serial.println("blynk_template_id: " + String(blynk_template_id));
|
|
Serial.println("blynk_template_name: " + String(blynk_template_name));
|
|
Serial.println("blynk_auth_token: " + String(blynk_auth_token));
|
|
}
|
|
|
|
// Initialize Blynk with obtained credentials
|
|
Blynk.begin(blynk_auth_token, ssid, pass);
|
|
|
|
// Menampilkan data yang digunakan ke Serial Monitor
|
|
Serial.println("Final data used:");
|
|
Serial.println("lokasi_id: " + String(lokasi_id));
|
|
Serial.println("blynk_template_id: " + String(blynk_template_id));
|
|
Serial.println("blynk_template_name: " + String(blynk_template_name));
|
|
Serial.println("blynk_auth_token: " + String(blynk_auth_token));
|
|
|
|
// Inisialisasi OTA
|
|
ArduinoOTA.begin();
|
|
buzzTwice();
|
|
uploadWireless();
|
|
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.print(" kWh-Meter ");
|
|
delay(2000);
|
|
lcd.clear();
|
|
|
|
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
|
// Ensure time is properly set up
|
|
// Wait for time to be set
|
|
struct tm timeinfo;
|
|
if (!getLocalTime(&timeinfo)) {
|
|
Serial.println("Failed to obtain time");
|
|
} else {
|
|
Serial.println("Time obtained successfully");
|
|
Serial.print("Current time: ");
|
|
Serial.printf("%02d-%02d-%04d %02d:%02d:%02d\n",
|
|
timeinfo.tm_mday,
|
|
timeinfo.tm_mon + 1,
|
|
timeinfo.tm_year + 1900,
|
|
timeinfo.tm_hour,
|
|
timeinfo.tm_min,
|
|
timeinfo.tm_sec);
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
ArduinoOTA.handle();
|
|
Blynk.run();
|
|
getDateTime();
|
|
unsigned long currentMillis = millis();
|
|
|
|
// Pernyataan debugging untuk memeriksa status relayState saat ini
|
|
Serial.print("relayState: ");
|
|
Serial.println(relayState);
|
|
if (relayState == HIGH) { // Mode Otomatis Aktif
|
|
controlRelayBasedOnSensor(); // Gunakan logika kontrol otomatis jika mode Otomatis
|
|
} else { // Mode Otomatis Nonaktif
|
|
digitalWrite(relayPin, LOW); // Matikan relay
|
|
}
|
|
|
|
float voltage = pzem.voltage();
|
|
float current = pzem.current();
|
|
float powerFactor = pzem.pf();
|
|
float power = voltage * powerFactor * current;
|
|
float hours = 1;
|
|
float energy = power * (hours / 1000);
|
|
|
|
if (power >= 450) { // Aktifkan buzzer jika daya lebih besar dari atau sama dengan 5 W
|
|
digitalWrite(buzzerPin, HIGH);
|
|
if (currentMillis - previousMillis >= buzzerDuration) {
|
|
digitalWrite(buzzerPin, LOW); // Matikan buzzer setelah 1 menit
|
|
previousMillis = currentMillis; // Perbarui waktu sebelumnya
|
|
}
|
|
} else {
|
|
digitalWrite(buzzerPin, LOW);
|
|
}
|
|
|
|
Serial.println("Pembacaan Sensor:");
|
|
Serial.print("Voltage: "); Serial.print(voltage); Serial.println(" V");
|
|
Serial.print("Current: "); Serial.print(current); Serial.println(" A");
|
|
Serial.print("Power Factor: "); Serial.println(powerFactor);
|
|
Serial.print("Power: "); Serial.print(power); Serial.println(" W");
|
|
Serial.print("Energy: "); Serial.print(energy); Serial.println(" kWh");
|
|
Serial.println();
|
|
|
|
if (isnan(voltage) || isnan(current) || isnan(powerFactor)) {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Error membaca");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("sensor");
|
|
} else {
|
|
float biaya = (energy * tarifPerKWh);
|
|
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Voltage: "); lcd.print(voltage); lcd.print("V");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Power: "); lcd.print(power); lcd.print("W");
|
|
delay(2000);
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Pf: "); lcd.print(powerFactor);
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Energy: "); lcd.print(energy); lcd.print("kWh");
|
|
delay(2000);
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Current: "); lcd.print(current); lcd.print("A");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Biaya: Rp. "); lcd.print(biaya, 2);
|
|
delay(2000);
|
|
|
|
unsigned long currentTime = millis();
|
|
if (currentTime - lastBlynkUpdateTime >= blynkUpdateInterval) {
|
|
// Upload data ke Blynk
|
|
Blynk.virtualWrite(V2, voltage);
|
|
Blynk.virtualWrite(V3, current);
|
|
Blynk.virtualWrite(V4, powerFactor);
|
|
Blynk.virtualWrite(V5, power);
|
|
Blynk.virtualWrite(V6, energy);
|
|
Blynk.virtualWrite(V7, biaya);
|
|
lastBlynkUpdateTime = currentTime;
|
|
}
|
|
|
|
if (currentTime - lastUploadTime >= uploadInterval) {
|
|
int lokasi_id_int = atoi(lokasi_id);
|
|
// Upload data ke server
|
|
sendHttpPost(lokasi_id_int, voltage, power, powerFactor, energy, current, biaya, created_at);
|
|
lastUploadTime = currentTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
void controlRelayBasedOnSensor() {
|
|
float power = pzem.power();
|
|
if (power >= 500) {
|
|
digitalWrite(relayPin, LOW); // Matikan relay jika daya lebih dari atau sama dengan 20 W
|
|
relayState = LOW; // Simpan status relay
|
|
} else {
|
|
digitalWrite(relayPin, HIGH); // Hidupkan relay jika daya kurang dari 20 W
|
|
relayState = HIGH; // Simpan status relay
|
|
}
|
|
}
|
|
|
|
void sendHttpPost(int lokasi_id, float voltage, float power, float powerFactor, float energy, float current, float biaya, const char* created_at) {
|
|
if (WiFi.status() == WL_CONNECTED) { // Pastikan WiFi terhubung
|
|
HTTPClient http;
|
|
Serial.println("Memulai HTTP POST...");
|
|
|
|
http.begin("https://kantorku.cloud/voltech/upload/sensor_data.php");
|
|
http.addHeader("Content-Type", "application/json");
|
|
|
|
StaticJsonDocument<200> jsonDoc;
|
|
jsonDoc["lokasi_id"] = lokasi_id;
|
|
jsonDoc["voltage"] = voltage;
|
|
jsonDoc["power"] = power;
|
|
jsonDoc["power_factor"] = powerFactor;
|
|
jsonDoc["energy"] = energy;
|
|
jsonDoc["current"] = current;
|
|
jsonDoc["biaya"] = biaya;
|
|
jsonDoc["created_at"] = created_at;
|
|
|
|
String jsonString;
|
|
serializeJson(jsonDoc, jsonString);
|
|
|
|
// Debugging: Cetak payload JSON
|
|
Serial.print("Payload JSON: ");
|
|
Serial.println(jsonString);
|
|
|
|
// Kirim permintaan POST dengan payload JSON
|
|
int httpResponseCode = http.POST(jsonString);
|
|
|
|
if (httpResponseCode > 0) {
|
|
Serial.print("Kode Respons HTTP: ");
|
|
Serial.println(httpResponseCode);
|
|
String response = http.getString();
|
|
Serial.println("Respons dari server: " + response);
|
|
} else {
|
|
Serial.print("Kode Kesalahan: ");
|
|
Serial.println(httpResponseCode);
|
|
}
|
|
|
|
|
|
http.end();
|
|
} else {
|
|
Serial.println("WiFi tidak terhubung");
|
|
}
|
|
}
|
|
|
|
void uploadWireless() {
|
|
ArduinoOTA
|
|
.onStart([]() {
|
|
String type;
|
|
if (ArduinoOTA.getCommand() == U_FLASH)
|
|
type = "sketch";
|
|
else // U_SPIFFS
|
|
type = "filesystem";
|
|
|
|
Serial.println("Start updating " + type);
|
|
})
|
|
.onEnd([]() {
|
|
Serial.println("\nEnd");
|
|
})
|
|
.onProgress([](unsigned int progress, unsigned int total) {
|
|
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
|
})
|
|
.onError([](ota_error_t error) {
|
|
Serial.printf("Error[%u]: ", error);
|
|
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
|
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
|
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
|
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
|
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
|
});
|
|
|
|
ArduinoOTA.begin();
|
|
|
|
Serial.println("Ready");
|
|
Serial.print("IP address: ");
|
|
Serial.println(WiFi.localIP());
|
|
}
|
|
|
|
void getDateTime() {
|
|
struct tm timeinfo;
|
|
if (!getLocalTime(&timeinfo)) {
|
|
Serial.println("Failed to obtain time");
|
|
snprintf(created_at, sizeof(created_at), "0000-00-00 00:00:00");
|
|
} else {
|
|
snprintf(created_at, sizeof(created_at), "%04d-%02d-%02d %02d:%02d:%02d",
|
|
timeinfo.tm_year + 1900,
|
|
timeinfo.tm_mon + 1,
|
|
timeinfo.tm_mday,
|
|
timeinfo.tm_hour,
|
|
timeinfo.tm_min,
|
|
timeinfo.tm_sec);
|
|
}
|
|
}
|
|
|
|
BLYNK_WRITE(V1) {
|
|
relayState = param.asInt(); // Mengubah status relay berdasarkan tombol Blynk V1
|
|
Serial.print("Relay state changed to: ");
|
|
Serial.println(relayState);
|
|
// Balikkan logika relay untuk modul relay aktif rendah
|
|
if (relayState == 1) {
|
|
digitalWrite(relayPin, LOW); // Hidupkan relay jika tombol Blynk diaktifkan
|
|
} else {
|
|
digitalWrite(relayPin, HIGH); // Matikan relay jika tombol Blynk dinonaktifkan
|
|
}
|
|
}
|
|
|
|
void buzzTwice() {
|
|
for (int i = 0; i < 2; i++) {
|
|
digitalWrite(buzzerPin, HIGH);
|
|
delay(200);
|
|
digitalWrite(buzzerPin, LOW);
|
|
delay(200);
|
|
}
|
|
}
|