MPPT Arduino ATMega8 Solar Charge Controller Ver01 Code
MPPT Arduino ATMega8 Solar Charge Controller Ver01 Code
//
// Arduino Peak Power Tracking Solar Charger by Ram Sankar Pillai (www.aaram-engineering.com)
//
// This software implements Tim Nolan's (www.timnolan.com) Peak Power Tracking Solar Charger using the
Arduino IDE
//
// 5/1/09 v1.00 First development version by Tim Nolan. Just getting something to work.
//
// 06/Mar/2015 code modified to suit Ram Sankar Pillai's pro PCB, included LED indication
//------------------------------------------------------------------------------------------------------
//#include <EEPROM.h>
LiquidCrystal lcd(7,8,0,1,2,3);
//------------------------------------------------------------------------------------------------------
// definitions
#define AVG_NUM 10 // number of iterations of the adc routine to average the adc readings
#define SOL_AMPS_SCALE 37 // the scaling value for raw adc reading to get solar amps scaled by 60 - (Scale
by 54 for MAX4173T, 22 for MAX4173F, 11 for MAX4173H)
#define SOL_VOLTS_SCALE 28 // the scaling value for raw adc reading to get solar volts scaled by 27
#define BAT_VOLTS_SCALE 28 // the scaling value for raw adc reading to get battery volts scaled by 27
//#define PWM_ENABLE_PIN 8 // pin used to control shutoff function of the IR2104 MOSFET driver
#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_INC 1 //the value the increment to the pwm value for the ppt algorithm
#define TRUE 1
#define FALSE 0
#define ON TRUE
#define ONE_SECOND 50000 //count for number of interrupt in 1 second on interrupt period of 20us
#define LOW_SOL_WATTS 5.00 //value of solar watts scaled by 100 so this is 5.00 watts
#define MIN_SOL_WATTS 1.00 //value of solar watts scaled by 100 so this is 1.00 watts
#define MIN_BAT_VOLTS 11.00 //value of battery voltage scaled by 100 so this is 11.00 volts
#define MAX_BAT_VOLTS 14.10 //value of battery voltage scaled by 100 so this is 14.10 volts
#define HIGH_BAT_VOLTS 13.60 //value of battery voltage scaled by 100 so this is 13.00 volts
#define yellowLed 10
#define greenLed 11
#define redLed 12
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b00000
};
byte battery[8]=
0b01110,
0b11011,
0b10001,
0b10001,
0b11111,
0b11111,
0b11111,
0b11111,
};
//------------------------------------------------------------------------------------------------------
// global variables
int delta = PWM_INC; // variable used to modify pwm duty cycle for the ppt algorithm
float old_sol_watts = 0; // solar watts from previous time through ppt routine scaled by 100
float watts, wattHours;
enum charger_mode {
charger_state; // enumerated variable that holds state for charger state machine
//------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
//
// bitClear(ADCSRA,ADPS0) ;
// bitClear(ADCSRA,ADPS1) ;
// bitSet(ADCSRA,ADPS2) ;
TCCR1B = TCCR1B & B11111000 | B00000001; // set timer 1 divisor to 1 for PWM frequency of 31372.55 Hz
wattHours = 1000;
pinMode(yellowLed, OUTPUT);
pinMode(greenLed, OUTPUT);
pinMode(redLed, OUTPUT);
pinMode(FAN_PIN,OUTPUT);
digitalWrite(yellowLed,LOW);
digitalWrite(greenLed,LOW);
digitalWrite(redLed,LOW);
digitalWrite(FAN_PIN,HIGH);
lcd.begin(16, 2); // set up coulumns and rows for LCD. Using a 20x4 display.
lcd.createChar(1,solar);
lcd.createChar(2,battery);
//------------------------------------------------------------------------------------------------------
// This routine reads and averages the analog inputs for this system, solar volts, solar amps and
// battery volts. It is called with the adc channel number (pin number) and returns the average adc
// value as an integer.
//------------------------------------------------------------------------------------------------------
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
//------------------------------------------------------------------------------------------------------
// This routine uses the Timer1.pwm function to set the pwm duty cycle. The routine takes the value in
// the variable pwm as 0-100 duty cycle and scales it to get 0-1034 for the Timer1 routine.
// There is a special case for 100% duty cycle. Normally this would be have the top MOSFET on all the time
// but the MOSFET driver IR2104 uses a charge pump to generate the gate voltage so it has to keep running
// all the time. So for 100% duty cycle I set the pwm value to 1023 - 1 so it is on 99.9% almost full on
//------------------------------------------------------------------------------------------------------
void set_pwm_duty(void) {
if (pwm > PWM_MAX) { // check limits of PWM duty cyle and set to
PWM_MAX
pwm = PWM_MAX;
else if (pwm < PWM_MIN) { // if pwm is less than PWM_MIN then set it to PWM_MIN
pwm = PWM_MIN;
analogWrite(PWM_PIN,(PWM_FULL * (long)pwm / 100)); // use Timer1 routine to set pwm duty cycle at 20uS
period
else if (pwm == PWM_MAX) { // if pwm set to 100% it will be on full but we have
analogWrite(PWM_PIN,(PWM_FULL - 1)); // keep switching so set duty cycle at 99.9% and slow down to
1000uS period
//------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
void lcd_display1(void) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(1);
//lcd.print(" ");
lcd.print(sol_volts,1);
lcd.print("V ");
lcd.print("0");
lcd.print(sol_amps*sol_volts,0);
lcd.print(sol_amps*sol_volts,0);
else {
lcd.print("00");
lcd.print("W ");
lcd.print("0");
lcd.print(sol_amps,1);
lcd.print(sol_amps,1);
else {
lcd.print("00.0");
lcd.print("A");
//lcd.print(sol_amps,1);
//lcd.print("A");
lcd.setCursor(0, 1);
lcd.write(2);
//lcd.print(" ");
lcd.print(bat_volts,1);
lcd.print("V ");
lcd.print("0");
lcd.print(sol_amps*bat_volts,0);
lcd.print(sol_amps*bat_volts,0);
else {
lcd.print("00");
lcd.print("W ");
if (disp_page==1){
lcd.print(wattHours,0);
lcd.setCursor(15,1);
lcd.print("h");
if(disp_page==2) {
lcd.print(pwm,0);
lcd.setCursor(15,1);
lcd.print("%");
}
//------------------------------------------------------------------------------------------------------
// 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. Then it adds on a rounding value before dividing to get
// the result scaled by 100 to give a fractional value of two decimal places. It also calculates the input
// watts from the solar amps times the solar voltage and rounds and scales that by 100 (2 decimal places) also.
//------------------------------------------------------------------------------------------------------
void read_data(void) {
sol_volts = read_adc(SOL_VOLTS_CHAN)*0.02705;
bat_volts = read_adc(BAT_VOLTS_CHAN)*0.02613;
sol_amps = (read_adc(SOL_AMPS_CHAN));
sol_amps = (sol_amps/1023.0)*5000;
sol_watts = sol_volts*sol_amps;
// temperature = (read_adc(TEMP_ADC)*0.488);
//------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------
void power(void)
{
msec = millis();
elasped_msec = msec - last_msec; //Calculate how long has past since last call of this function
//------------------------------------------------------------------------------------------------------
// This routine to indicate charger state using Green, Yellow, and red LEDs
//------------------------------------------------------------------------------------------------------
void ledIndication(void) {
if (charger_state == off){
digitalWrite(greenLed,LOW);
digitalWrite(yellowLed,LOW);
digitalWrite(redLed,HIGH);
digitalWrite(greenLed,LOW);
digitalWrite(yellowLed,HIGH);
digitalWrite(redLed,LOW);
digitalWrite(greenLed,HIGH);
digitalWrite(yellowLed,LOW);
digitalWrite(redLed,LOW);
//------------------------------------------------------------------------------------------------------
// 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.
//
// On State - this is charger state for MIN_SOL_WATTS < solar watts < LOW_SOL_WATTS. This state is probably
// happening at dawn and dusk when the 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 = 100% to get the most of low
// 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
// Off State - This is state that the charger enters when solar watts < MIN_SOL_WATTS. The charger goes into this
// state when it gets dark and 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. When the charger off
// state is first entered all it does is decrement off_count for OFF_NUM times. This is done because if the battery
// is disconnected (or battery fuse is blown) it takes some time before the battery voltage changes enough so we
can tell
// that the battery is no longer connected. This off_count gives some time for battery voltage to change so we can
// tell this.
//------------------------------------------------------------------------------------------------------
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
analogWrite(PWM_PIN,0);
else if (bat_volts > MAX_BAT_VOLTS) { // > 14.1V 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
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
else if (bat_volts > MAX_BAT_VOLTS) { //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
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
pwm += delta; // add delta to change PWM duty cycle for PPT algorythm
old_sol_watts = sol_watts; // load old_watts with current watts value for next time
break;
case bat_float:
if (sol_watts < MIN_SOL_WATTS) { //if watts input from the solar panel is less than
set_pwm_duty();
analogWrite(PWM_PIN,0);
else if (bat_volts > MAX_BAT_VOLTS) { //since we're in the battery float state if the battery voltage
pwm -= 1; //is above the float voltage back off the pwm to lower it
set_pwm_duty();
}
else if (bat_volts < MAX_BAT_VOLTS) { //else if the battery voltage is less than the float voltage
set_pwm_duty();
if (pwm >= 100) { //if pwm gets up to 100 it means we can't keep the battery at
charger_state = bulk; //float voltage so jump to charger bulk state to charge the battery
break;
case off: //when we jump into the charger off state, off_count is set with OFF_NUM
if (off_count > 0) { //this means that we run through the off state OFF_NUM of times with out doing
off_count--; //anything, this is to allow the battery voltage to settle down to see if the
else if ((bat_volts > HIGH_BAT_VOLTS) && (bat_volts < MAX_BAT_VOLTS) && (sol_volts > bat_volts)) {
charger_state = bat_float; //if battery voltage is still high and solar volts are high
else if ((bat_volts > MIN_BAT_VOLTS) && (bat_volts < MAX_BAT_VOLTS) && (sol_volts > bat_volts)) {
pwm = PWM_START; //if battery volts aren't quite so high but we have solar volts
break;
default:
analogWrite(PWM_PIN,0);
break;
}
}
//------------------------------------------------------------------------------------------------------
// Main loop.
// Right now the number of times per second that this main loop runs is set by how long the printing to
// the serial port takes. You can speed that up by speeding up the baud rate.
// You can also run the commented out code and the charger routines will run once a second.
//------------------------------------------------------------------------------------------------------
power();
// delay(50);
// read_data();
prev_millis = millis();
ledIndication();
lcd_display1();
if (sol_amps > 2) {
digitalWrite(FAN_PIN,HIGH);
else{
digitalWrite(FAN_PIN,LOW);
if (disp_page==1) disp_page=2;
else disp_page=1;