Arduino MPPT V3
Arduino MPPT V3
-------------------
// ARDUINO MPPT SOLAR CHARGE CONTROLLER (Version-3)
// Author: Debasish Dutta/deba168
// www.opengreenenergy.in
//
// This code is for an arduino Nano based Solar MPPT charge controller.
// This code is a modified version of sample code from www.timnolan.com
// updated 06/07/2015
//
// Mods by Aplavins 19/06/2015
////
Specifications : /////////////////////////////////////////////////////////////////
/////////////////////////////////////
//
// 1.Solar panel power = 50W
//
// 2.Rated Battery Voltage= 12V ( lead acid type )
// 3.Maximum current = 5A
//
// 5. In put Voltage = Solar panel with Open circuit voltage from 17 to 25V
//
///////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////
/////////
Definitions ///////////////////////////////////////////////////////////////////////
//////////////////////////
// Turn this on to use the ESP8266 chip. If you set this to 0, the periodic updates
will not happen
#define ENABLE_DATALOGGER 0
#define SOL_AMPS_SCALE 0.026393581 // the scaling value for raw adc reading
to get solar amps // 5/(1024*0.185)
#define SOL_VOLTS_SCALE 0.029296875 // the scaling value for raw adc reading
to get solar volts // (5/1024)*(R1+R2)/R2 // R1=100k and R2=20k
#define BAT_VOLTS_SCALE 0.029296875 // the scaling value for raw adc reading
to get battery volts
#define PWM_PIN 9 // the output pin for the pwm (only pin 9
avaliable for timer 1 at 50kHz)
#define PWM_ENABLE_PIN 8 // pin used to control shutoff function of the
IR2104 MOSFET driver (hight the mosfet driver is on)
#define PWM_FULL 1023 // the actual value used by the Timer1
routines for 100% pwm duty cycle
#define PWM_MAX 100 // the value for pwm duty cyle 0-100%
#define PWM_MIN 60 // the value for pwm duty cyle 0-100% (below
this value the current running in the system is = 0)
#define PWM_START 90 // the value for pwm duty cyle 0-100%
#define PWM_INC 1 //the value the increment to the pwm value for
the ppt algorithm
#define TRUE 1
#define FALSE 0
#define ON TRUE
#define OFF FALSE
//---------------------------------------------------------------------------------
---------------------
//Defining led pins for indication
#define LED_GREEN 11
#define LED_YELLOW 12
#define LED_RED 13
//---------------------------------------------------------------------------------
--------------------
// Defining load control pin
#define LOAD_PIN 6 // pin-2 is used to control the load
//---------------------------------------------------------------------------------
--------------------
// Defining lcd back light pin
#define BACK_LIGHT_PIN 5 // pin-5 is used to control the lcd back light
// ---------------------------For
ESP8266--------------------------------------------------------------
//---------------------------------------------------------------------------------
---------------------
/////////////////////////////////////////BIT MAP
ARRAY//////////////////////////////////////////////////
//---------------------------------------------------------------------------------
----------------------
byte battery_icons[6][8]=
{{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b10001,
0b10001,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b10001,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
}};
#define SOLAR_ICON 6
byte solar_icon[8] = //icon for termometer
{
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b00000
};
#define PWM_ICON 7
byte _PWM_icon[8]=
{
0b11101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10111,
};
byte backslash_char[8]=
{
0b10000,
0b10000,
0b01000,
0b01000,
0b00100,
0b00100,
0b00010,
0b00010,
};
//---------------------------------------------------------------------------------
----------------------
// global variables
//---------------------------------------------------------------------------------
---------------------
// This routine is automatically called at powerup/reset
//---------------------------------------------------------------------------------
---------------------
// create the LCD special characters. Characters 0-5 are the various battery
fullness icons
// icon 7 is for the PWM icon, and icon 8 is for the solar array
lcd.backlight();
for (int batchar = 0; batchar < 6; ++batchar) {
lcd.createChar(batchar, battery_icons[batchar]);
}
lcd.createChar(PWM_ICON,_PWM_icon);
lcd.createChar(SOLAR_ICON,solar_icon);
lcd.createChar('\\', backslash_char);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
Timer1.initialize(20); // initialize timer1, and set a 20uS period
Timer1.pwm(PWM_PIN, 0); // setup pwm on pin 9, 0% duty cycle
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow
interrupt
Serial.begin(9600); // open the serial port at 9600 bps:
ser.begin(9600); // enable software serial
ser.println("AT+RST"); // reset ESP8266
pwm = PWM_START; //starting value for pwm
pinMode(BACK_LIGHT_PIN, INPUT);
pinMode(LOAD_PIN,OUTPUT);
digitalWrite(LOAD_PIN,LOW); // default load state is OFF
digitalWrite(BACK_LIGHT_PIN,LOW); // default LCd back light is OFF
//---------------------------------------------------------------------------------
---------------------
// Main loop
//---------------------------------------------------------------------------------
---------------------
void loop()
{
read_data(); // read data from inputs
run_charger(); // run the charger state machine
print_data(); // print data
load_control(); // control the connected load
led_output(); // led indication
lcd_display(); // lcd display
#if ENABLE_DATALOGGER
wifi_datalog(); // sends data to thingspeak
#endif
}
//---------------------------------------------------------------------------------
---------------------
// This routine reads and averages the analog inputs for this system, solar volts,
solar amps and
// battery volts.
//---------------------------------------------------------------------------------
---------------------
int read_adc(int channel){
int sum = 0;
int temp;
int i;
for (i=0; i<AVG_NUM; i++) { // loop through reading raw adc values
AVG_NUM number of times
temp = analogRead(channel); // read the input pin
sum += temp; // store sum for averaging
delayMicroseconds(50); // pauses for 50 microseconds
}
return(sum / AVG_NUM); // divide sum by AVG_NUM to get average and
return it
}
//---------------------------------------------------------------------------------
---------------------
// This routine reads all the analog input values for the system. Then it
multiplies them by the scale
// factor to get actual value in volts or amps.
//---------------------------------------------------------------------------------
---------------------
void read_data(void) {
//---------------------------------------------------------------------------------
---------------------
// This is interrupt service routine for Timer1 that occurs every 20uS.
//
//---------------------------------------------------------------------------------
---------------------
void callback()
{
if (interrupt_counter++ > ONE_SECOND) { // increment interrupt_counter
until one second has passed
interrupt_counter = 0; // reset the counter
seconds++; // then increment seconds counter
}
}
//---------------------------------------------------------------------------------
---------------------
// This routine uses the Timer1.pwm function to set the pwm duty cycle.
//---------------------------------------------------------------------------------
---------------------
void set_pwm_duty(void) {
//---------------------------------------------------------------------------------
---------------------
// This routine is the charger state machine. It has four states on, off, bulk and
float.
// It's called once each time through the main loop to see what state the charger
should be in.
// The battery charger can be in one of the following four states:
//
// On State - this is charger state for MIN_SOL_WATTS < solar watts <
LOW_SOL_WATTS. In this state isthe solar
// watts input is too low for the bulk charging state but not low enough to go
into the off state.
// In this state we just set the pwm = 99.9% to get the most of low amount of
power available.
// Bulk State - this is charger state for solar watts > MIN_SOL_WATTS. This is
where we do the bulk of the battery
// charging and where we run the Peak Power Tracking alogorithm. In this state
we try and run the maximum amount
// of current that the solar panels are generating into the battery.
// Float State - As the battery charges it's voltage rises. When it gets to the
MAX_BAT_VOLTS we are done with the
// bulk battery charging and enter the battery float state. In this state we
try and keep the battery voltage
// at MAX_BAT_VOLTS by adjusting the pwm value. If we get to pwm = 100% it
means we can't keep the battery
// voltage at MAX_BAT_VOLTS which probably means the battery is being drawn
down by some load so we need to back
// into the bulk charging mode.
// Off State - This is state that the charger enters when solar watts <
MIN_SOL_WATTS. The charger goes into this
// state when there is no more power being generated by the solar panels. The
MOSFETs are turned
// off in this state so that power from the battery doesn't leak back into the
solar panel.
//---------------------------------------------------------------------------------
---------------------
void run_charger(void) {
switch (charger_state) {
case on:
if (sol_watts < MIN_SOL_WATTS) { // if watts input from
the solar panel is less than
charger_state = off; // the minimum solar
watts then
off_count = OFF_NUM; // go to the charger
off state
TURN_OFF_MOSFETS;
}
else if (bat_volts > (BATT_FLOAT - 0.1)) { // else if the battery
voltage has gotten above the float
charger_state = bat_float; // battery float
voltage go to the charger battery float state
}
else if (sol_watts < LOW_SOL_WATTS) { // else if the solar
input watts is less than low solar watts
pwm = PWM_MAX; // it means there is
not much power being generated by the solar panel
set_pwm_duty(); // so we just set the pwm =
100% so we can get as much of this power as possible
} // and stay in the
charger on state
else {
pwm = ((bat_volts * 10) / (sol_volts / 10)) + 5; // else if we are
making more power than low solar watts figure out what the pwm
charger_state = bulk; // value should be and
change the charger to bulk state
}
break;
case bulk:
if (sol_watts < MIN_SOL_WATTS) { // if watts input from
the solar panel is less than
charger_state = off; // the minimum solar
watts then it is getting dark so
off_count = OFF_NUM; // go to the charger
off state
TURN_OFF_MOSFETS;
}
else if (bat_volts > BATT_FLOAT) { // else if the battery
voltage has gotten above the float
charger_state = bat_float; // battery float
voltage go to the charger battery float state
}
else if (sol_watts < LOW_SOL_WATTS) { // else if the solar
input watts is less than low solar watts
charger_state = on; // it means there is
not much power being generated by the solar panel
TURN_ON_MOSFETS; // so go to charger on
state
}
else { // this is where we do
the Peak Power Tracking ro Maximum Power Point algorithm
if (old_sol_watts >= sol_watts) { // if previous watts
are greater change the value of
delta = -delta; // delta to make pwm increase
or decrease to maximize watts
}
pwm += delta; // add delta to change
PWM duty cycle for PPT algorythm (compound addition)
old_sol_watts = sol_watts; // load old_watts with
current watts value for next time
set_pwm_duty(); // set pwm duty cycle to pwm
value
}
break;
case bat_float:
//---------------------------------------------------------------------------------
-------------------------------------
/////////////////////////////////////////////LOAD
CONTROL/////////////////////////////////////////////////////
//---------------------------------------------------------------------------------
-------------------------------------
void load_control(){
#if LOAD_ALGORITHM == 0
// turn on loads at night when the solar panel is not producing power
// as long as the battery voltage is above LVD
load_on(sol_watts < MIN_SOL_WATTS && bat_volts > LVD);
#else
// dump excess solar energy into the load circuit
load_on(sol_watts > MIN_SOL_WATTS && bat_volts > BATT_FLOAT);
#endif
}
Serial.print(seconds,DEC);
Serial.print(" ");
Serial.print("Charging = ");
if (charger_state == on) Serial.print("on ");
else if (charger_state == off) Serial.print("off ");
else if (charger_state == bulk) Serial.print("bulk ");
else if (charger_state == bat_float) Serial.print("float");
Serial.print(" ");
Serial.print("pwm = ");
if(charger_state == off)
Serial.print(0,DEC);
else
Serial.print(pwm,DEC);
Serial.print(" ");
Serial.print("\n\r");
//delay(1000);
}
//---------------------------------------------------------------------------------
----------------
//---------------------------------Led
Indication--------------------------------------------------
//---------------------------------------------------------------------------------
----------------
// light an individual LED
// we remember which one was on before in last_lit and turn it off if different
void light_led(char pin)
{
static char last_lit;
if (last_lit == pin)
return;
if (last_lit != 0)
digitalWrite(last_lit, LOW);
digitalWrite(pin, HIGH);
last_lit = pin;
}
// display the current state via LED as follows:
// YELLOW means overvoltage (over 14.1 volts)
// RED means undervoltage (under 11.9 volts)
// GREEN is between 11.9 and 14.1 volts
void led_output(void)
{
static char last_lit;
if(bat_volts > 14.1 )
light_led(LED_YELLOW);
else if(bat_volts > 11.9)
light_led(LED_GREEN);
else
light_led(LED_RED);
}
//---------------------------------------------------------------------------------
---------------------
//-------------------------- LCD DISPLAY
--------------------------------------------------------------
//---------------------------------------------------------------------------------
----------------------
void lcd_display()
{
static bool current_backlight_state = -1;
back_light_pin_State = digitalRead(BACK_LIGHT_PIN);
if (current_backlight_state != back_light_pin_State) {
current_backlight_state = back_light_pin_State;
if (back_light_pin_State == HIGH)
lcd.backlight();// finish with backlight on
else
lcd.noBacklight();
}
if (back_light_pin_State == HIGH)
{
time = millis(); // If any of the buttons are pressed,
save the time in millis to "time"
}
lcd.setCursor(0, 1);
lcd.print(sol_volts);
lcd.print("V ");
lcd.setCursor(0, 2);
lcd.print(sol_amps);
lcd.print("A");
lcd.setCursor(0, 3);
lcd.print(sol_watts);
lcd.print("W ");
lcd.setCursor(8, 1);
lcd.print(bat_volts);
lcd.setCursor(8,2);
if (charger_state == on)
lcd.print("on ");
else if (charger_state == off)
lcd.print("off ");
else if (charger_state == bulk)
lcd.print("bulk ");
else if (charger_state == bat_float)
{
lcd.print(" ");
lcd.setCursor(8,2);
lcd.print("float");
}
//-----------------------------------------------------------
//--------------------Battery State Of Charge ---------------
//-----------------------------------------------------------
int pct = 100.0*(bat_volts - 11.3)/(12.7 - 11.3);
if (pct < 0)
pct = 0;
else if (pct > 100)
pct = 100;
lcd.setCursor(12,0);
lcd.print((char)(pct*5/100));
lcd.setCursor(8,3);
pct = pct - (pct%10);
lcd.print(pct);
lcd.print("% ");
//---------------------------------------------------------------------
//------------------Duty Cycle-----------------------------------------
//---------------------------------------------------------------------
lcd.setCursor(15,0);
lcd.print("PWM");
lcd.setCursor(19,0);
lcd.write(PWM_ICON);
lcd.setCursor(15,1);
lcd.print(" ");
lcd.setCursor(15,1);
if( charger_state == off)
lcd.print(0);
else
lcd.print(pwm);
lcd.print("% ");
//----------------------------------------------------------------------
//------------------------Load Status-----------------------------------
//----------------------------------------------------------------------
lcd.setCursor(15,2);
lcd.print("Load");
lcd.setCursor(15,3);
if (load_status)
{
lcd.print("On ");
}
else
{
lcd.print("Off ");
}
spinner();
backLight_timer(); // call the backlight timer function in
every loop
}
void backLight_timer(){
if((millis() - time) <= 15000) // if it's been less than the 15 secs,
turn the backlight on
lcd.backlight(); // finish with backlight on
else
lcd.noBacklight(); // if it's been more than 15 secs, turn
the backlight off
}
void spinner(void) {
static int cspinner;
static char spinner_chars[] = { '*','*', '*', ' ', ' '};
cspinner++;
lcd.print(spinner_chars[cspinner%sizeof(spinner_chars)]);
}
//-------------------------------------------------------------------------
//----------------------------- ESP8266 WiFi ------------------------------
//--------------------------Plot System data on thingspeak.com-------------
//-------------------------------------------------------------------------
void wifi_datalog()
{
// thingspeak needs 15 sec delay between updates
static int lastlogged;
if ( seconds - lastlogged < 16 )
return;
lastlogged = seconds;
// convert to string
char buf[16];
String strTemp = dtostrf( sol_volts, 4, 1, buf);
Serial.println(strTemp);
// TCP connection
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += "184.106.153.149"; // api.thingspeak.com
cmd += "\",80";
ser.println(cmd);
if(ser.find((char *)"Error")){
Serial.println("AT+CIPSTART error");
return;
}
if(ser.find((char *)">")){
ser.print(getStr);
}
else{
ser.println("AT+CIPCLOSE");
// alert user
Serial.println("AT+CIPCLOSE");
}
}