/**
SoC - SoH
EruP(c)12-2023
*/
#include <LiquidCrystal.h>
#include <Keypad.h>
#include <Servo.h>
/* Display */
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
/* Keypad setup */
const byte KEYPAD_ROWS = 4;
const byte KEYPAD_COLS = 4;
byte rowPins[KEYPAD_ROWS] = {5, 4, 3, 2};
byte colPins[KEYPAD_COLS] = {A3, A2, A1, A0};
char keys[KEYPAD_ROWS][KEYPAD_COLS] = {
{'1', '2', '3', '+'},
{'4', '5', '6', '-'},
{'7', '8', '9', '*'},
{'.', '0', '=', '/'}
};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS);
uint64_t value = 0;
#define AH 10 // kapasitas awal baterai
#define minVoL 10.5 // Tegangan minimal baterai (SoC=0%)
#define maxVoL 12.70 // Tegangan minimal baterai (SoC=100%)
float Tabel[12][2]=
{
{100,12.7},
{95,12.6},
{90,12.5},
{80,12.42},
{70,12.32},
{60,12.2},
{50,12.06},
{40,11.9},
{30,11.75},
{20,11.58},
{10,11.31},
{0,10.5}
};
float HitungSoC(float VoL)
{
for(short int i=1;i++;i<=11)
{
if(VoL>=Tabel[i][1])
{
float x2=Tabel[i-1][1];
float x1=Tabel[i][1];
float y2=Tabel[i-1][0];
float y1=Tabel[i][0];
float SoC=(y2-y1)/(x2-x1)*(VoL-x1)+y1;
return SoC;
continue;
}
}
}
float xHitungVoL(float SoC)
{
for(short int i=1;i++;i<=11)
{
if(SoC>=Tabel[i][0])
{
float x2=Tabel[i-1][0];
float x1=Tabel[i][0];
float y2=Tabel[i-1][1];
float y1=Tabel[i][1];
float VoL=(y2-y1)/(x2-x1)*(SoC-x1)+y1;
return VoL;
continue;
}
}
}
float xHitungSoC(float VoL)
{
//return (VoL-minVoL)/(maxVoL-minVoL)*100;
//float S=-11.577*pow(VoL,4) + 535.87*pow(VoL,3) - 9263*VoL*VoL + 70912*VoL - 202944;
double S=-11.57677316721360*pow(VoL,4) + 535.86600856623000*pow(VoL,3) + -9263.02898113957000*pow(VoL,2) + 70912.12858742210000*VoL + -202943.92722954200000;
//https://agrimetsoft.com/regressions/Polynomial#toolSection
//double S=-90.258314061228500*pow(VoL,6) + 6360.458436326890000*pow(VoL,5) + -186627.314513787000000*pow(VoL,4) + 2918445.556913080000000*pow(VoL,3) + -25652797.055340600000000*VoL*VoL + 120169321.089649000000000*VoL + -234374671.718637000000000;
return S;
//return S>100?100:S<0?0:S;
}
float HitungVoL(float SoC)
{
// return (maxVoL-minVoL)*SoC/100+minVoL;
//float V = 3E-09*pow(SoC,5) - 9E-07*pow(SoC,4) + 9E-05*pow(SoC,3) - 0.0044*SoC*SoC + 0.1133*SoC + 10.508;
//float V=0.000004*pow(SoC,3) - 0.0008*pow(SoC,2) + 0.0611*SoC + 10.61;
//float V = -4E-11*pow(SoC,6) + 1E-08*pow(SoC,5) - 2E-06*pow(SoC,4) + 0.0002*pow(SoC,3) - 0.0059*SoC*SoC + 0.1258*SoC + 10.502;
//float V=1E-11*pow(SoC,6) - 3E-09*pow(SoC,5) + 2E-07*pow(SoC,4) + 5E-06*pow(SoC,3) - 0.0016*pow(SoC,2) + 0.0812*SoC + 10.524;
double V= -0.000000000035400*pow(SoC,6) + 0.000000013941310*pow(SoC,5) + -0.000002111161922*pow(SoC,4) + 0.000156174617911*pow(SoC,3) + -0.005945345569180*SoC*SoC + 0.125803689165569*SoC + 10.501726897959300;
return V>maxVoL?maxVoL:V<minVoL?minVoL:V;
}
float Baterai(float Arus)
{
const float SoH=100;//45; // % Kesehatan baterai saat ini
const float Isi=100;
static float KapBatt=AH*SoH/100; // Kapasitas real baterai
static float Kap=KapBatt*Isi/100; // Kapasitas Isi
const float rd=0.05; // R Dalam baterai
static unsigned long WaktuLalu=micros();
unsigned long WaktuSekarang=micros();
//Serial.println("wL="+String(WaktuLalu)+"us, ws="+String(WaktuSekarang)+"us, dw="+String(((float)WaktuSekarang-(float)WaktuLalu)/1000000.0/10.0)+"h");
//float dWaktu=WaktuSekarang;
//Serial.println(dWaktu);
//dWaktu-=WaktuLalu;
//Serial.println(dWaktu);
//dWaktu/=1000000.0;
//Serial.println(dWaktu);
//dWaktu/=3600.0;
//dWaktu*=3600.0;
//Serial.println(dWaktu);
float dWaktu=((float)WaktuSekarang-WaktuLalu)/1000000/3600;
WaktuLalu=WaktuSekarang;
Kap-=(Arus*dWaktu);
if(Kap<=0) Kap=0;
float SoC=Kap/KapBatt*100;
float VL=HitungVoL(SoC)-rd*Arus;
Serial.println("dT="+String(dWaktu*360)+"s, Arus="+String(Arus)+"A, SoC="+String(SoC)+"%, Isi="+String(Kap)+"Ah, VL="+String(VL)+"V");
return VL;
}
void setup() {
Serial.begin(115200);
lcd.begin(20, 4);
}
void xloop()
{
for(float SoC=0;SoC<=100;SoC+=5)
{
float VoL=HitungVoL(SoC);
Serial.println("SoC="+String(SoC)+"%, VoL="+String(VoL)+"V");
delay(1000);
}
}
void loop()
{
static float Arus=0;
char key=keypad.getKey();
if(key)
{
Arus=(key-'0')*10;
if(Arus<0) Arus=0;
//Serial.println("Arus="+String(Arus)+"A");
}
lcd.setCursor(0,0);
lcd.print("IL="+String(Arus)+"A ");
float Vin=Baterai(Arus);
lcd.setCursor(0,1);
lcd.print("VL="+String(Vin)+"V ");
static float VoL;
float VL;
static short int ArusMin=0;
const float Imin=0;
static float rd=0;
if(Arus<=Imin)
{
ArusMin=1;
VoL=Vin;
//Serial.println("VoL="+String(VoL)+"V");
}
else
{
VL=Vin;
//Serial.println("VoL="+String(VoL)+"V VL="+String(VL)+"V");
if(ArusMin==1)
{
ArusMin=0;
rd=(VoL-VL)/Arus;
lcd.setCursor(0,3);
lcd.print("rd="+String(rd)+"R ");
//Serial.println("rd="+String(rd)+"R");
}
VoL=VL+rd*Arus;
//Serial.println("VoL="+String(VoL)+"V");
}
lcd.setCursor(10,0);
lcd.print("VoL="+String(VoL)+"V ");
float SoC=HitungSoC(VoL);
lcd.setCursor(10,1);
lcd.print("SoC="+String((int)ceil(SoC))+"% ");
static float SoCL=-1;
if(SoCL==-1) SoCL=SoC;
static unsigned long WaktuLalu=micros();
unsigned long Waktu=micros();
float dWaktu=((float)Waktu-WaktuLalu)/1000000/3600;
WaktuLalu=Waktu;
static float Q=0;
Q+=Arus*dWaktu;
float dSoC=SoCL-SoC;
Serial.println("SoCL="+String(SoCL)+"%, SoC="+String(SoC)+"%, dSoC="+String(dSoC)+"%, Arus="+String(Arus)+"A, dWaktu="+String(dWaktu*3600)+"s, Q="+String(Q)+"Ah");
if(dSoC>0)
{
float KapBatt=Q/dSoC*100;
float SoH=KapBatt/AH*100;
if(SoH>100) SoH=100;
lcd.setCursor(10,3);
lcd.print("SoH="+String((int)SoH)+"% ");
}
delay(1000);
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
keypad:R1
keypad:R2
keypad:R3
keypad:R4
keypad:C1
keypad:C2
keypad:C3
keypad:C4
lcd:VSS
lcd:VDD
lcd:V0
lcd:RS
lcd:RW
lcd:E
lcd:D0
lcd:D1
lcd:D2
lcd:D3
lcd:D4
lcd:D5
lcd:D6
lcd:D7
lcd:A
lcd:K
r1:1
r1:2
lcd1:VSS
lcd1:VDD
lcd1:V0
lcd1:RS
lcd1:RW
lcd1:E
lcd1:D0
lcd1:D1
lcd1:D2
lcd1:D3
lcd1:D4
lcd1:D5
lcd1:D6
lcd1:D7
lcd1:A
lcd1:K