MPPT charge controller on Arduino

Reading time: 7 min.

When recharging batteries, it is necessary to ensure the correct current characteristics in order to prevent overcharging. This increases the life of the battery within the solar panel – battery system. When the critical voltage is exceeded, the regulator disconnects the energy source, and the same happens when the battery is fully charged. This paper presents an example prototype of an Arduino-based solar battery charger that operates on the principle of maximum power point tracking (MPPT).

Functions and purpose of the MPPT controller

The MPPT controller is responsible for charging the battery in different modes, protects the battery from damage due to overcharging and also protects the solar panel from overcurrent. Other functions of the MPPT controller are battery overload protection (undervoltage lockout) and charge level tracking. Today’s modern batteries, such as lead-gel batteries, require a 3-stage charge.

When the battery is deeply discharged, bulk charging is initiated, i.e. with a current corresponding to the battery capacity. In this mode, the battery is charged with a constant current up to about 80% of its total capacity. When a certain voltage threshold is reached, the controller switches the charging process to absorption mode. In this mode, the battery is charged with a constant voltage, but with an increasingly lower current. When the absorption phase is complete, the charge controller switches to voltage hold mode and charges the battery with a current that is 1% of the battery capacity divided by one hour, e.g. for a 2Ah battery this would be 2mA.

In our prototype, we will use a battery with a nominal voltage of 12V and a capacity of 2Ah, which can be charged with a maximum voltage of 14.4V and a current of 2A.

Building a prototype MPPT charge controller based on Arduino and its principle of functioning

Our MPPT charge controller must be able to read the output and input voltages and currents, and perform the necessary calculations to enable the necessary optimisation of the charging process. For this purpose, we will use the dedicated ACS712 module as a current sensor. The voltage boost converter, on the other hand, will be controlled using a PWM signal source from the Arduino and a MOSFET transistor or electromagnetic relay, which will switch the load on and off. To read the charging parameters, we will use a 4×20 LCD display (four lines of twenty characters each). You will also need an inverter to step down the voltage to 5V to power the Arduino board.

 data-lazy-srcset=

CHECK AT STORE

MPPT charge controller – program code for measuring and stabilising the charge current at a constant level

The following program code is intended to control the limitation of the charging current to 1A. Therefore, if too high a charging voltage is connected, the MPPT controller is designed to limit the charging current to a software-set value. This is done by appropriately decreasing and increasing the fill factor of the PWM waveform controlling the gate of the MOSFET transistor to relatively stabilise the charging current to 1A.

Program code:

					//The program controls the stabilisation of the battery charging current by the MPPT charging controller
#include <Wire.h> // Wire.h library initiation
#include <LiquidCrystal_I2C.h> //LCD library initiation
LiquidCrystal_I2C lcd(0x27,20,4);  //Addressing the LCD to the I2C bus
//Declaration of constants
#define LCD_refresh_rate 1000 //LCD display illumination every 1s
//Declaration of inputs
#define solar_current_in A0 //curret measurement - analog input A0
//Declaration of outputs
#define PWM_out 10
#define load_enable 2
//Declaration of variables
int pwm_value = 0; //the initial fill factor of the MOSFET control signal
float solar_current = 0; //initial charging current
float current_factor = 0.185; //Default value declared by the manufacturer of the ACS712 sensor
unsigned int before_millis = 0;
unsigned int now_millis = 0;
void setup(){
  pinMode(solar_current_in,INPUT); //charging current measuring pin as input
  pinMode(PWM_out,OUTPUT); //PWM charging control pin as output
  digitalWrite(PWM_out,LOW); //PWM pin set low - MOSFET does not conduct
  TCCR1B = TCCR1B & B11111000 | B00000001; //timer 1 set to 31372.55Hz frequency
  Serial.begin(9600); //enable serial transmission
  lcd.init(); //activation of LCD display
  lcd.backlight(); //switching on the LCD display backlight
  before_millis = millis; //LCD display time resolution
}
void loop(){
  solar_current = get_solar_current(10);
  now_millis = millis();
  if(now_millis - before_millis > LCD_refresh_rate)
    {
    before_millis = now_millis;
    //lcd.clear(); //cleaning the contents of the LCD display
    lcd.setCursor(0,0); //cursor positioning
    lcd.print(solar_current,2); //displaying the current at the output of the solar panel
    lcd.print("A"); //current in Amperes
    }
//Stabilisation of the constant charging current
    if(solar_current > 1.0) //for charging currents greater than 1.0A
    {
    pwm_value--;
    pwm_value = constrain(pwm_value,0,254); //PWM modulation range from 0% to 99%
    } else {
    pwm_value++;
    pwm_value = constrain(pwm_value,0,254);
    }
    analogWrite(PWM_out,pwm_value); //value of the fill factor
//measuring the number of samples
    float get_solar_current(int n_samples)
    {
        float Sensor_voltage; //sensor voltage
        float current =0;
        for(int i=0; i < n_samples; i++)
        {
        Sensor_voltage = analogRead(solar_current_in) * (5.0 / 1023.0);
        current = current + (Sensor_voltage-2.5)/current_factor;
        } //sensor voltage reading
    }
current = current/n_samples; //calculation of the number of current samples
return(current);
}
				
			

MPPT charge controller – program code for measuring and stabilising the charge voltage at a constant level

The next part of the program code is responsible for maintaining a voltage with a constant charging value. In the case of our project, this will be a value of 12V. The code considers all three phases of the battery charging process.

Program code:

					//The program controls the stabilisation of the battery charging voltage by the MPPT charging controller
#include <Wire.h> // Wire.h library initiation
#include <LiquidCrystal_I2C.h> // LCD library initiation
LiquidCrystal_I2C lcd(0x27,20,4);  //Addressing the LCD to the I2C bus
//Declaration of inputs
#define battery_voltage_in A2
//Declaration of outputs
#define PWM_out 10
#define load_enable 2
//Declaration of variables
float bat_voltage = 0; //initial battery charging voltage
int pwm_value = 0; //initial signal fill factor of MOSFET
unsigned int before_millis = 0;
unsigned int now_millis = 0;
#define LCD_refresh_rate 1000 //LCD display refreshing every 1s
void setup(){
  pinMode(battery_voltage_in,INPUT); //pin for measuring the battery charging voltage as an input
  pinMode(PWM_out,OUTPUT); //PWM pin as output
  digitalWrite(PWM_out,LOW); //PWM pin flipped to low state
  TCCR1B = TCCR1B & B11111000 | B00000001; //timer 1 set to 31372.55 Hz
  Serial.begin(9600); //enabling serial transmission
  lcd.init(); //Activation of LCD display
  lcd.backlight(); //Switching on the LCD display backlight
  before_millis = millis;
}
void loop(){
  bat_voltage = get_battery_voltage(10); //battery voltage based on the PWM signal fill factor at pin 10
  now_millis = millis();
  if(now_millis - before_millis > LCD_refresh_rate)
  {
    before_millis = now_millis;
    //lcd.clear(); //cleaning the contents of the LCD display
    lcd.setCursor(0,0); //resetting the cursor position
    lcd.print(bat_voltage,2); //battery voltage
    lcd.print("V"); //display of result in Volts
  }
//Charge voltage stabilisation control
  if(bat_voltage > 12.0){ //Checking the battery over voltage condition
    pwm_value++;
    pwm_value = constrain(pwm_value,0,254);
  }
  else {
    pwm_value--;
    pwm_value = constrain(pwm_value,0,254);
  }
  analogWrite(PWM_out,pwm_value); //PWM output control
}
//Measurement and probing of battery voltage values
float get_battery_voltage(int n_samples)
{
  float voltage = 0;
  for(int i=0; i < n_samples; i++)
  {
    voltage += (analogRead(battery_voltage_in) * (5.0 / 1023.0) * 7.85); //voltage measurement
  }
  voltage = voltage/n_samples; //calculation of the number of voltage samples
  return(voltage);
}
				
			

MPPT charge controller – program code for measuring and stabilising the maximum power point

The last part of the program code for the MPPT controller, is responsible for calculating and stabilising the maximum power point at which the most optimal conditions for charging the battery occur. The program performs comparative measurements of currents and voltages and switches between charging modes when certain conditions are met.

Program code:

					//The programme controls the stabilisation of the maximum power point of the MPPT charge controller and switches between charging modes
#include <Wire.h> //Initiation of Wire.h library
#include <LiquidCrystal_I2C.h> //LCD library initiation
LiquidCrystal_I2C lcd(0x27,20,4); //Addressing the LCD to the I2C bus
//Initiation of variables
uint8_t Battery[8]  = {0x0E, 0x1B, 0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x1F};
uint8_t Panel[8]  = {0x1F, 0x15, 0x1F, 0x15, 0x1F, 0x15, 0x1F, 0x00};
uint8_t Pwm[8]  = {0x1D, 0x15, 0x15, 0x15, 0x15,0x15, 0x15, 0x17};
uint8_t Flash[8]  = {0x01, 0x02, 0x04, 0x1F, 0x1F, 0x02, 0x04, 0x08};
//Initiation of constants
#define bulk_voltage_max 14.5 //max. voltage in bulk mode
#define bulk_voltage_min 13 //min. voltage in bulk mode
#define absorption_voltage 14.7 //voltage in absorption mode
#define float_voltage_max 13 //max. voltage in back-up mode
#define battery_min_voltage 10 //min. battery voltage
#define solar_min_voltage 19 //min. solar panel voltage
#define charging_current 2.0 //min. charging current
#define absorption_max_current 2.0 //max. absorption current
#define absorption_min_current 0.1 //min. absorption current
#define float_voltage_min 13.2 //min. voltage in back-up mode
#define float_voltage 13.4 //set voltage in back-up mode
#define float_max_current 0.12 //max. voltage in back-up mode
#define LCD_refresh_rate 1000 //LCD display refreshing every 1s
byte BULK = 0; //assignment of numbers to individual charging modes
byte ABSORPTION = 1;
byte FLOAT = 2;
byte mode = 0; //by default, we start loading from bulk mode
//configuration of analogue inputs for measurements
#define solar_voltage_in A1 //solar panel voltage - pin A1
#define solar_current_in A0 //solar panel output current - pin A0
#define battery_voltage_in A2 //battery voltage - pin A2
//output config.
#define PWM_out 10 //PWM output - pin D10
#define load_enable 2 //starting charging - pin D2
//initiation of variables
float bat_voltage = 0; //battery voltage
int pwm_value = 0; //fill factor
float solar_current = 0; //charing current
float current_factor = 0.185; //current constant for the ACS712 5A sensor
float solar_voltage = 0; //solar panel voltage
float solar_power = 0; //solar panel power
String load_status = "OFF"; //loading status turned off
int pwm_percentage = 0; //Percentage of fill ratio
unsigned int before_millis = 0;
unsigned int now_millis = 0;
String mode_str = "BULK";
void setup(){
  pinMode(solar_voltage_in,INPUT); //pin configuration as input
  pinMode(solar_current_in,INPUT);
  pinMode(battery_voltage_in,INPUT);
  pinMode(PWM_out,OUTPUT); //PWM pin configuration as output
  digitalWrite(PWM_out,LOW); //PWM pin set low - MOSFET does not conduct
  pinMode(load_enable,OUTPUT); //charge switch output
  digitalWrite(load_enable,LOW); //switching off the charging (relay)
  TCCR1B = TCCR1B & B11111000 | B00000001; //timer 1 set to 31372.55Hz
  Serial.begin(9600); //enable serial transmission
  lcd.init(); //switching on the LCD display
  lcd.backlight(); //switching on the LCD display backlight
  lcd.createChar(0, Battery);
  lcd.createChar(1, Panel);
  lcd.createChar(2, Pwm);
  lcd.createChar(3, Flash);
  before_millis = millis; //LCD display time resolution
}
void loop(){
  solar_voltage = get_solar_voltage(15); //solar panel voltage
  bat_voltage =   get_battery_voltage(15); //battery voltage
  solar_current = get_solar_current(15); //solar panel current
  solar_power = bat_voltage * solar_current; //solar panel power
  pwm_percentage = map(pwm_value,0,255,0,100);
//display of measurements on LCD screen
  now_millis = millis();
  if(now_millis - before_millis > LCD_refresh_rate)
  {
    before_millis = now_millis;
    lcd.clear();
    lcd.setCursor(0,0); //cursor positioned on column 0 row 0
    lcd.write(1); //writing "1" on the LCD - measurement for the solar panel.
    lcd.print(" "); //space
    lcd.print(solar_voltage,2); //solar panel voltage
    lcd.print("V"); //voltage in Volts
    lcd.print("    "); //space (4 times)
    lcd.write(0); //writing "0" on LCD - measurement for battery
    lcd.print(" ");  //space
    lcd.print(bat_voltage,2); //battery voltage
    lcd.print("V"); //voltage in Volts
    lcd.setCursor(0,1); //cursor positioned on column 0 row 1
    lcd.print("  "); //space (4 times)
    lcd.print(solar_current,2); //measuring the current for the solar panel.
    lcd.print("A"); //current in Amperes
    lcd.print("     LOAD "); //load
    lcd.print(load_status); //load status 
    lcd.setCursor(0,2); //cursor positioned on column 0 row 2
    lcd.print("  "); //two times space
    lcd.print(solar_power,2); //power from solar panel
    lcd.print("W"); //power in Watts
    lcd.print("     PWM "); //displaying "PWM"
    lcd.print(pwm_percentage); //Percentage of fill ratio
    lcd.print("%"); //percentage sign display
    lcd.setCursor(0,3); //cursor positioned on column 0 row 3
    lcd.print(mode_str);
  }
  if(bat_voltage < battery_min_voltage){
    digitalWrite(load_enable,LOW); //below the minimum voltage the load is switched off
    load_status  = "OFF"; //load status - off
  }
  else{
    digitalWrite(load_enable,HIGH); //when the battery is charged, we switch on the load
    load_status  = "ON"; //load status - on
  }
//Battery back-up mode at the end of charging
//For battery voltages below the backup voltage, activate DC charging in BULK mode
  if(mode == FLOAT){
    if(bat_voltage < float_voltage_min){
      mode = BULK; //bulk mode set on
      mode_str = "BULK";
    }
    else{
      if(solar_current > float_max_current){ //when the solar panel current exceeds the holding current, switch to DC charging in BULK mode
        mode = BULK; //switching on bulk mode
        mode_str = "BULK";
      }//End if >
      else{
        if(bat_voltage > float_voltage){
          pwm_value--;
          pwm_value = constrain(pwm_value,0,254); //for a battery voltage higher than the backup voltage, reduce the discharge by 1
        }
        else {
          pwm_value++;
          pwm_value = constrain(pwm_value,0,254); //for a battery voltage lower than the backup voltage, increase the filling factor by 1
        }
      }//End else > float_max_current //otherwise increase the charging current in the voltage holding state
      analogWrite(PWM_out,pwm_value);
    }
  }//END of mode == FLOAT //deactivation of voltage support mode
  //Mode change from bulk to absorption
  else{
    if(bat_voltage < bulk_voltage_min){
      mode = BULK;
      mode_str = "BULK"; //activation of bulk charging mode
    }
    else if(bat_voltage > bulk_voltage_max){
      mode_str = "ABSORPTION";
      mode = ABSORPTION; //activation of the absorption charging mode
    }
//Activation of bulk mode
    if(mode == BULK){
      if(solar_current > charging_current){ //if the current of the solar panel is higher than the charging current, reduce the boost by 1
        pwm_value--;
        pwm_value = constrain(pwm_value,0,254);
      }
      else { ////if the current of the solar panel. less than the charging current, increase the filling ratio by 1
        pwm_value++;
        pwm_value = constrain(pwm_value,0,254);
      }
      analogWrite(PWM_out,pwm_value);
    }//End of mode == BULK 
   //absorbtion mode
    if(mode == ABSORPTION){
      if(solar_current > absorption_max_current){
        pwm_value--;
        pwm_value = constrain(pwm_value,0,254); //if the solar panel current exceeds the maximum absorption current, reduce the filling factor by 1
      }//End if > absorption_max_current ////if the solar panel current exceeds the maximum absorption current, reduce the filling factor by 1
      else{
        if(bat_voltage > absorption_voltage){
          pwm_value--;
          pwm_value = constrain(pwm_value,0,254); //if the battery voltage exceeds the absorption voltage, reduce the filling ratio by 1 upwards
        }
        else {
          pwm_value++;
          pwm_value = constrain(pwm_value,0,254); // //if the battery voltage is lower than the absorption voltage, increase the discharge by 1 upwards
        }
        if(solar_current < absorption_min_current){ //if the solar panel current is less than the minimum absorption charging current
          mode = FLOAT; //activation of voltage support modes
          mode_str = "FLOAT";
        }
      }//End else > absorption_max_current //if the current of the solar panel exceeds the maximum absorption charging current
      analogWrite(PWM_out,pwm_value);
    }// End of mode == absorption_max_current //deactivation of voltage support mode
  }//END of else mode == FLOAT
  //Serial.println(bat_voltage); //battery voltage
}//End void loop
//Measurement of the solar panel voltage by voltage probing
float get_solar_voltage(int n_samples)
{
  float voltage = 0;
  for(int i=0; i < n_samples; i++)
  {
    voltage += (analogRead(solar_voltage_in) * (5.0 / 1023.0) * 8.0); //calculation of the solar panel voltage for ADC
  }
  voltage = voltage/n_samples;
  if(voltage < 0){voltage = 0;}
  return(voltage);
}
//Battery voltage measurement by voltage probing
float get_battery_voltage(int n_samples)
{
  float voltage = 0;
  for(int i=0; i < n_samples; i++)
  {
    voltage += (analogRead(battery_voltage_in) * (5.0 / 1023.0) * 7.85); //calculation of the voltage for an ADC
  }
  voltage = voltage/n_samples;
  if(voltage < 0){voltage = 0;}
  return(voltage);
}
//Panel current measurement by voltage probing
float get_solar_current(int n_samples)
{
  float Sensor_voltage;
  float current =0;
  for(int i=0; i < n_samples; i++)
  {
    Sensor_voltage = analogRead(solar_current_in) * (5.0 / 1023.0); //calculation of the voltage drop for an ADC
    current = current + (Sensor_voltage-2.5)/current_factor; //calculation of current from voltage drop
  }
  current = current/n_samples;
  if(current < 0){current = 0;}
  return(current);
}
				
			

Arduino as MPPT controller – summary

 data-lazy-srcset=

The MPPT charge controller concept presented here illustrates a simple prototype that, while performing sufficiently, still has some limitations due to the low efficiency of the photovoltaic panels, among other things. However, as solar panel technology improves, the efficiency of the panels will increase and, combined with the use of the MPPT algorithm, photovoltaics will become even more economically advantageous. Over time, such technology will perform even better in powering loads with increased electricity consumption.

How useful was this post?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 3

No votes so far! Be the first to rate this post.

Share:

Picture of Maciej Figiel

Maciej Figiel

Versatile, he is eager to take on challenges because he thinks it is the fastest way to progress. He values contact with nature and an active rest. Automotive and new technologies enthusiast.

See more:

Leave a Reply

Your email address will not be published. Required fields are marked *

For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.