Skip to content

Instantly share code, notes, and snippets.

@fajarlabs
Last active February 6, 2024 04:33
Show Gist options
  • Save fajarlabs/28aea467ee464b6e796917fa02b9908a to your computer and use it in GitHub Desktop.
Save fajarlabs/28aea467ee464b6e796917fa02b9908a to your computer and use it in GitHub Desktop.
#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