Last active
February 6, 2024 04:33
-
-
Save fajarlabs/28aea467ee464b6e796917fa02b9908a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <EEPROM.h> | |
#include <LiquidCrystal_I2C.h> | |
#include <PZEM004Tv30.h> | |
#include "EEvar.h" | |
#include <TimeOut.h> | |
#define EEADDR 166 | |
#define PERKWH 1820 | |
#define RELAY_PIN 6 | |
#define RELAY_PIN_SUPPORT 7 | |
#define RESET_PIN 4 // reset ZERO | |
#define BUZZER_PIN A3 | |
#define INTERRUPT_COIN_PIN 2 | |
#define MAX_COUNTER 2 | |
#define LCD_TYPE_COLS 16 // jenis LCD 16x2 | |
#define LCD_TYPE_ROWS 2 // jenis LCD 16x2 | |
#if !defined(PZEM_RX_PIN) && !defined(PZEM_TX_PIN) | |
#define PZEM_RX_PIN 9 | |
#define PZEM_TX_PIN 10 | |
#endif | |
typedef struct { | |
float harga_1_watt; | |
float dapatkan_kredit_watt; | |
float sisa_kredit_daya_dalam_detik; | |
float harga_perkwh; | |
long uang; | |
} StoreData; | |
typedef struct { | |
float voltage; | |
float current; | |
float power; | |
float energy; | |
float frequency; | |
float pf; | |
} HistoryData; | |
EEstore<float> eeSisaKreditDayaDetik(0.0); | |
SoftwareSerial pzemSWSerial(PZEM_RX_PIN, PZEM_TX_PIN); | |
PZEM004Tv30 pzem(pzemSWSerial); | |
HistoryData hd; | |
// timeout 0 (check coin) | |
TimeOut timeout0; | |
void callback0(); | |
int impulsCount = 0; | |
// timeout 1 ( auto restart ) | |
TimeOut timeout1; | |
void callback1(); | |
// reset program | |
void(* resetFunc) (void) = 0; //declare reset function @ address 0 | |
LiquidCrystal_I2C lcd(0x27, 20, 4); | |
// lock interrupt | |
boolean insert = false; | |
// Init Var Power Sensor | |
float voltage = 0; | |
float current = 0; | |
float power = 0; | |
float energy = 0; | |
float frequency = 0; | |
float pf = 0; | |
int holdReset = MAX_COUNTER; // berapa lama BUTTON di tekan | |
bool isBuzzer = false; | |
bool isStarted = false; | |
bool isReady = false; | |
void setup() { | |
// put your setup code here, to run once: | |
Serial.begin(9600); | |
initMyLCD(); | |
pinMode(INTERRUPT_COIN_PIN, INPUT_PULLUP); | |
attachInterrupt(digitalPinToInterrupt(2), incomingImpuls, RISING); | |
writeLCD(LCD_TYPE_COLS, 0, 0, "EV-Charger Koin"); | |
writeLCD(LCD_TYPE_COLS, 0, 1, " ----##---- "); | |
delay(1000); | |
// relay pin relay | |
pinMode(RELAY_PIN, OUTPUT); | |
pinMode(RELAY_PIN_SUPPORT, OUTPUT); | |
// buzzer pin | |
pinMode(BUZZER_PIN, OUTPUT); | |
// coin acceptor interrupt | |
pinMode(RESET_PIN, INPUT_PULLUP); | |
delay(500); | |
// set relay to low | |
digitalWrite(RELAY_PIN, LOW); | |
digitalWrite(RELAY_PIN_SUPPORT, LOW); | |
delay(500); | |
// for (int i = 0; i < LCD_TYPE_ROWS; i++) { | |
// writeLCD(LCD_TYPE_COLS, 0, i, ""); | |
// } | |
isStarted = true; | |
// restart setiap 24 jam sekali | |
long longDelay = 86400 * 1000; | |
timeout1.timeOut(longDelay, callback1); //delay, callback function | |
} | |
void loop() { | |
TimeOut::handler(); | |
if (digitalRead(RESET_PIN) == LOW) { | |
if (wait(1000)) { | |
holdReset -= 1; | |
if (holdReset < 1) { | |
float resetSisaKredit = 0; | |
eeSisaKreditDayaDetik << resetSisaKredit; | |
Serial.println(F("*MSG*RESET BERHASIL")); | |
holdReset = MAX_COUNTER; | |
for (int i = 0; i < 3; i++) { | |
writeLCD(LCD_TYPE_COLS, 0, 0, ""); | |
writeLCD(LCD_TYPE_COLS, 0, 1, ""); | |
delay(500); | |
writeLCD(LCD_TYPE_COLS, 0, 0, " RESET BERHASIL "); | |
buzzerFlipFlop(true); | |
delay(500); | |
} | |
buzzerFlipFlop(false); | |
writeLCD(LCD_TYPE_COLS, 0, 0, ""); | |
writeLCD(LCD_TYPE_COLS, 0, 1, ""); | |
} | |
} | |
} else { | |
holdReset = MAX_COUNTER; | |
} | |
if (wait(1000)) { | |
if (isReady == false) { | |
writeLCD(LCD_TYPE_COLS, 0, 0, ""); | |
writeLCD(LCD_TYPE_COLS, 0, 1, ""); | |
writeLCD(LCD_TYPE_COLS, 0, 0, "Memuat data..."); | |
delay(500); | |
} | |
// Check if the data is valid | |
if (isnan(voltage)) { | |
Serial.println(F("*ERR*Error reading voltage")); | |
} else if (isnan(current)) { | |
Serial.println(F("*ERR*Error reading current")); | |
} else if (isnan(power)) { | |
Serial.println(F("*ERR*Error reading power")); | |
} else if (isnan(energy)) { | |
Serial.println(F("*ERR*Error reading energy")); | |
} else if (isnan(frequency)) { | |
Serial.println(F("*ERR*Error reading frequency")); | |
} else if (isnan(pf)) { | |
Serial.println(F("*ERR*Error reading power factor")); | |
} else { | |
// Read the data from the sensor | |
voltage = pzem.voltage(); Serial.println(voltage); | |
current = pzem.current(); | |
power = pzem.power(); | |
energy = pzem.energy(); | |
frequency = pzem.frequency(); | |
pf = pzem.pf(); | |
if (isReady == false) { | |
isReady = true; | |
writeLCD(LCD_TYPE_COLS, 0, 0, ""); | |
writeLCD(LCD_TYPE_COLS, 0, 1, ""); | |
} | |
if (voltage > 0) { | |
// debug power vars | |
// show_power_vars(voltage, current, power, energy, frequency, pf); | |
float sisaKreditDayaDetikTemp = 0; | |
eeSisaKreditDayaDetik >> sisaKreditDayaDetikTemp; | |
if (sisaKreditDayaDetikTemp >= 1) { | |
float estimasi_habis = 0; | |
float beban_daya = 0; | |
float powerA = hitung_beban_daya(voltage, current); | |
float powerB = power; | |
// ambil daya yang paling sesuai | |
// daya dari perhitungan P = V*I atau P = SENSOR jika tidak watt terlalu kecil | |
if (powerA > powerB){ | |
beban_daya = powerA; | |
} else { | |
beban_daya = powerB; | |
} | |
// periksa jika sisa kredit daya kosong/null/error | |
if (sisaKreditDayaDetikTemp < 1 || isnan(sisaKreditDayaDetikTemp)) { | |
Serial.println("Credit is empty"); | |
} else { | |
estimasi_habis = hitung_estimasi_habis(sisaKreditDayaDetikTemp, beban_daya); | |
} | |
if (beban_daya > sisaKreditDayaDetikTemp) { | |
digitalWrite(RELAY_PIN, LOW); | |
digitalWrite(RELAY_PIN_SUPPORT, LOW); | |
Serial.println(F("*MSG*Switch status : OFF")); | |
buzzerFlipFlop(true); | |
} else { | |
digitalWrite(RELAY_PIN, HIGH); | |
digitalWrite(RELAY_PIN_SUPPORT, HIGH); | |
sisaKreditDayaDetikTemp = sisaKreditDayaDetikTemp - beban_daya; | |
Serial.println(F("*MSG*Switch status : ON")); | |
buzzerFlipFlop(false); | |
} | |
/* | |
Serial.print("Estimasi habis : "); | |
Serial.println(estimasi_habis); | |
Serial.print("Saldo daya dalam detik : "); | |
Serial.print(sisaKreditDayaDetikTemp); | |
Serial.println(" Watt/Second"); | |
Serial.print("Beban daya : "); | |
Serial.print(beban_daya); | |
Serial.println(" Watt"); | |
*/ | |
float sisa_daya_aktual = sisa_daya_ke_watt(sisaKreditDayaDetikTemp); | |
/* | |
Serial.print("Sisa daya aktual : "); | |
Serial.print(sisa_daya_aktual); | |
Serial.println(" Watt"); | |
*/ | |
writeLCD(4, 0, 0, String(int(voltage)) + String("V")); | |
writeLCD(5, 5, 0, String(current) + String("A")); | |
writeLCD(5, 11, 0, String(int(power)) + String("W")); | |
writeLCD(6, 0, 1, "PULSA:"); | |
float convertToKWH = sisa_daya_aktual / 1000; | |
writeLCD(11, 6, 1, String(convertToKWH) + String("KWH")); | |
hd.voltage = voltage; | |
hd.current = current; | |
hd.power = power; | |
hd.energy = energy; | |
hd.frequency = frequency; | |
hd.pf = pf; | |
simpan_rekaman(hd, EEADDR); | |
eeSisaKreditDayaDetik << sisaKreditDayaDetikTemp; | |
} else { | |
writeLCD(4, 0, 0, String(int(voltage)) + String("V")); | |
writeLCD(5, 5, 0, String(current) + String("A")); | |
writeLCD(5, 11, 0, String(int(power)) + String("W")); | |
writeLCD(6, 0, 1, "PULSA:"); | |
writeLCD(11, 6, 1, String("0KWH")); | |
digitalWrite(RELAY_PIN, LOW); | |
digitalWrite(RELAY_PIN_SUPPORT, LOW); | |
} | |
} else { | |
writeLCD(LCD_TYPE_COLS, 0, 0, "No voltage"); | |
} | |
} | |
} | |
} | |
void incomingImpuls() { | |
impulsCount = impulsCount + 1; | |
timeout0.cancel(); | |
timeout0.timeOut(300, callback0); //delay, callback function | |
} | |
void callback0() { | |
int coinUang = 0; | |
if(impulsCount == 1){ | |
Serial.println("Coin 1"); | |
} | |
if(impulsCount == 5) { | |
coinUang = 1000; | |
Serial.println("Coin 2"); | |
} | |
if(impulsCount == 10){ | |
coinUang = 500; | |
Serial.println("Coin 3"); | |
} | |
Serial.println(F("*MSG*Add Coin")); | |
StoreData sd = konversi_uang_ke_sisa_daya(coinUang, PERKWH); | |
float _sisaKreditDayaDetik = 0; | |
eeSisaKreditDayaDetik >> _sisaKreditDayaDetik; | |
float _NewWisaKreditDayaDetik = _sisaKreditDayaDetik + sd.sisa_kredit_daya_dalam_detik; | |
eeSisaKreditDayaDetik << _NewWisaKreditDayaDetik; | |
//float sisa_daya_aktual = sisa_daya_ke_watt(sisaKreditDayaDetikTemp); | |
/* | |
Serial.print("Sisa daya aktual : "); | |
Serial.print(sisa_daya_aktual); | |
Serial.println(" Watt"); | |
*/ | |
impulsCount = 0; | |
} | |
void callback1() { | |
resetFunc(); | |
} | |
StoreData konversi_uang_ke_sisa_daya(long uang, float harga_per_kwh) { | |
StoreData obj; | |
obj.harga_1_watt = harga_per_kwh / 1000; | |
obj.dapatkan_kredit_watt = uang / obj.harga_1_watt; | |
obj.sisa_kredit_daya_dalam_detik = obj.dapatkan_kredit_watt * 3600; | |
obj.harga_perkwh = harga_per_kwh; | |
obj.uang = uang; | |
return obj; | |
} | |
float sisa_daya_ke_watt(float sisa_daya) { | |
return sisa_daya / 3600; | |
} | |
float hitung_beban_daya(float voltage, float current) { | |
return voltage * current; | |
} | |
float hitung_estimasi_habis(float sisa_kredit_daya_dalam_detik, float beban_daya) { | |
if (beban_daya < 1) | |
return 0; | |
else | |
return (sisa_kredit_daya_dalam_detik / beban_daya) / 3600; | |
} | |
void show_vars(StoreData *p) { | |
Serial.print("Uang dari koin acceptor : Rp."); | |
Serial.println(p->uang); | |
Serial.print("Harga per-Kwh : Rp."); | |
Serial.println(p->harga_perkwh); | |
Serial.print("Harga 1 watt per-Rupiah : Rp."); | |
Serial.println(p->harga_1_watt); | |
Serial.print("Mendapatkan Watt : "); | |
Serial.print(p->dapatkan_kredit_watt); | |
Serial.println(" watt/seconds"); | |
Serial.print("Total kredit daya dalam detik selama 1 jam : "); | |
Serial.print(p->sisa_kredit_daya_dalam_detik); | |
Serial.println(" watt/seconds"); | |
Serial.print("Sisa dalam watt : "); | |
Serial.print(sisa_daya_ke_watt(p->sisa_kredit_daya_dalam_detik)); | |
Serial.println(" watt"); | |
} | |
bool wait(unsigned long duration) { | |
static unsigned long startTime; | |
static bool isStarted = false; | |
if (isStarted == false) { | |
startTime = millis(); | |
isStarted = true; | |
return false; | |
} | |
if (millis() - startTime >= duration) { | |
isStarted = false; | |
return true; | |
} | |
return false; | |
} | |
void writeLCDLine(int col, int row, String text) { | |
lcd.setCursor(col, row); | |
lcd.print(text); | |
} | |
void clearLCDLine(int row, int total_cols) { | |
for (int n = 0; n < total_cols; n++) { | |
lcd.setCursor(n, row); | |
lcd.print(" "); | |
} | |
lcd.setCursor(0, row); // set cursor in the beginning of deleted line | |
} | |
void reverse(char *x, int begin, int end) { | |
char c; | |
if (begin >= end) | |
return; | |
c = *(x + begin); | |
*(x + begin) = *(x + end); | |
*(x + end) = c; | |
reverse(x, ++begin, --end); | |
} | |
String rupiah(long a) { | |
String ans; | |
String n = String(a); | |
int count = 0; | |
char buff[12]; | |
for (int i = n.length() - 1; i >= 0; i--) { | |
ans += n[i]; | |
count++; | |
if (count == 3) { | |
ans += ('.'); | |
count = 0; | |
} | |
} | |
if (ans.length() % 4 == 0) { | |
ans.remove(ans.length() - 1, 1); | |
} | |
ans.toCharArray(buff, 12); | |
reverse(buff, 0, ans.length() - 1); | |
return String(buff); | |
} | |
void simpan_rekaman(HistoryData hd, int eeaddr) { | |
EEPROM.put(eeaddr, hd); | |
} | |
HistoryData dapatkan_rekaman(int eeaddr) { | |
HistoryData hd; | |
EEPROM.get(eeaddr, hd); | |
return hd; | |
} | |
void show_power_vars(float voltage, float current, float power, float energy, float frequency, float pf) { | |
/* | |
Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V"); | |
Serial.print("Current: "); Serial.print(current); Serial.println("A"); | |
Serial.print("Power: "); Serial.print(power); Serial.println("W"); | |
Serial.print("Energy: "); Serial.print(energy,3); Serial.println("kWh"); | |
Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz"); | |
Serial.print("PF: "); Serial.println(pf); | |
*/ | |
} | |
void initMyLCD() { | |
lcd.init(); | |
lcd.backlight(); | |
lcd.clear(); | |
} | |
void writeLCD(int max_char, int cols, int rows, String message) { | |
lcd.setCursor(cols, rows); | |
if (message.length() > max_char) { | |
for (int i = 0; i < max_char; i++) { | |
message.concat(" "); | |
} | |
lcd.print("ERR"); | |
} else { | |
int total_space = max_char - message.length(); | |
if (total_space > 0) { | |
for (int i = 0; i < total_space; i++) { | |
message.concat(" "); | |
} | |
lcd.print(message); | |
} else { | |
lcd.print(message); | |
} | |
} | |
} | |
void buzzerFlipFlop(bool set_on) { | |
if (set_on == true) { | |
if (isBuzzer == false) { | |
digitalWrite(BUZZER_PIN, HIGH); | |
isBuzzer = true; | |
} else { | |
digitalWrite(BUZZER_PIN, LOW); | |
isBuzzer = false; | |
} | |
} else { | |
digitalWrite(BUZZER_PIN, LOW); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment