VAIBHAV DUBEY
Published © Apache-2.0

"Dual-Phase Interleaved Maximum Power Point Tracking System

"MPPT maximizes solar panel output by continuously adjusting load to match peak power point, boosting charging efficiency significantly. '

IntermediateFull instructions providedOver 4 days13
"Dual-Phase Interleaved Maximum Power Point Tracking System

Story

Read more

Code

Complete Project Code

C/C++
//ESP32 MPPT Firmware
#include "driver/ledc.h"        //- ledc.h
#include "esp_timer.h"          //- High Resolution Timer (ESP Timer)
#include <Adafruit_ADS1X15.h>   //- ADS1115/ADS1015 ADC Library (By: Adafruit)
#include "MovingAverage.h"      //- MovingAverage Filter By Ian Carey
#include <U8g2lib.h>            //- U8g2lib Library For Display
#include <Wire.h>               //- Library Requires For IIC Communication
ledc_timer_config_t ledc_timer;
ledc_channel_config_t ledc_channel;
ledc_timer_config_t ledc_timer_2;
ledc_channel_config_t ledc_channel_2;
Adafruit_ADS1115 ads;
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
#define SDA             5   //SYSTEM PARAMETER - Serial Data
#define SCL             4   //SYSTEM PARAMETER - Serial Clock
#define PWM_PIN         7   //SYSTEM PARAMETER - Choose any available PWM-capable pin on your ESP32
#define PWM_PIN_2       8   //SYSTEM PARAMETER - Choose any available PWM-capable pin on your ESP32
#define BCCU            9   //SYSTEM PARAMETER - BACKFLOW CURRENT CONTROL UNIT Relay pin.
#define TempSensor      10  //SYSTEM PARAMETER - Temperature Sensor GPIO Pin
MovingAverage filter1(20);  // Output Voltage Filter Value
MovingAverage filter2(50);  // Input Current Filter Value
MovingAverage filter3(20);  // Input Voltage Filter Value
float
PV_VoltageMax        = 90.000,    //  USER PARAMETER - Maximum PV Voltage (Input V)
PV_VoltageMin        = 16.000,    //  USER PARAMETER - Minimum PV Voltage (Input V)
PV_CurrentMax        = 20.000,    //  USER PARAMETER - Maximum PV Current (Input A)
BatteryVoltageMax    = 14.000,    //  USER PARAMETER - Maximum Battery Charging Voltage (Output V)
BatteryCurrentMax    = 30.000,    //  USER PARAMETER - Maximum Battery Current (Output A)
temperatureMax       = 75;        //  USER PARAMETER - Overtemperature, System Shudown When Exceeded (deg C)
int
avgCountTS             = 500,     //  CALIB PARAMETER - Temperature Sensor Average Sampling Count
OLED_Update_micros     = 2000000, //  CALIB PARAMETER - Time interval refresh rate for OLED display (us), Default = 2s.
OLED_Toggle_micros     = 5000000, //  CALIB PARAMETER - Time interval for showing next screen on OLED Display
ReconnectTime_micros   = 10000000,//  CALIB PARAMETER - Time delay for reconnecting PV (us), Default = 10s.
RoutineInterval_micros = 250000;  //  CALIB PARAMETER - Time Interval Refresh Rate For Routine Functions (us)
float
inVoltageDivRatio    = 59.808,    //  CALIB PARAMETER - Input voltage divider sensor ratio (change this value to calibrate voltage sensor)
outVoltageDivRatio   = 24.4970,   //  CALIB PARAMETER - Output voltage divider sensor ratio (change this value to calibrate voltage sensor)
ntcResistance        = 10000.00,   //  CALIB PARAMETER - NTC temp sensor's resistance.
efficiencyRate       = 0.9500,    //  CALIB PARAMETER - Theroretical Buck Efficiency (% decimal)
currentMidPoint      = 2.040,     //  CALIB PARAMETER - Current Sensor Midpoint (V)
currentSensV         = 0.1000,    //  CALIB PARAMETER - Current Sensor Sensitivity (mV/A)
voltageDropout       = 1.0000,    //  CALIB PARAMETER - Buck regulator's dropout voltage
voltageBatteryThresh = 1.5000,    //  CALIB PARAMETER
voltageBatteryMin    = 8.000;     //  CALIB PARAMETER - Minimum Battery voltage
bool
BNC                  = 0,         // SYSTEM PARAMETER - Battery not connected
IUV                  = 0,         // SYSTEM PARAMETER - Input under voltage flag
IOV                  = 0,         // SYSTEM PARAMETER - Input over voltage flag
IOC                  = 0,         // SYSTEM PARAMETER - Input over current flag
OOV                  = 0,         // SYSTEM PARAMETER - Output over voltage flag
OTE                  = 0,         // SYSTEM PARAMETER - Over temperature flag
Buck_Enable          = 0;         // SYSTEM PARAMETER - Buck Enable Status
int
avgStoreTS           = 0,         // SYSTEM PARAMETER - Temperature Sensor uses non invasive averaging, this is used an accumulator for mean averaging
temperature          = 0,         // SYSTEM PARAMETER - Temperature in celsius
sampleStoreTS        = 0,         // SYSTEM PARAMETER - TS AVG nth Sample
screen               = 0;         // SYSTEM PARAMETER - Used for displaying parameters on OLED
float
dutycycle            = 0.0000,    // SYSTEM PARAMETER - PWM Dutycycle
VSI                  = 0.0000,    // SYSTEM PARAMETER - Raw input voltage sensor ADC voltage
VSO                  = 0.0000,    // SYSTEM PARAMETER - Raw output voltage sensor ADC voltage
CSI                  = 0.0000,    // SYSTEM PARAMETER - Raw current sensor ADC voltage
TS                   = 0.0000,    // SYSTEM PARAMETER - Raw temperature sensor ADC value
TSlog                = 0.0000,    // SYSTEM PARAMETER - Part of NTC thermistor thermal sensing code
voltageInput         = 0.0000,    // SYSTEM PARAMETER - Input voltage (solar voltage)
voltageInputPrev     = 0.0000,    // SYSTEM PARAMETER - Previously stored input voltage variable for MPPT algorithm
voltageOutput        = 0.0000,    // SYSTEM PARAMETER - Output voltage (battery voltage)
currentInput         = 0.0000,    // SYSTEM PARAMETER - Input current (solar current)
currentInputPrev     = 0.0000,    // SYSTEM PARAMETER - Previously stored input current variable for MPPT algorithm
currentOutput        = 0.0000,    // SYSTEM PARAMETER - Output current (battery or charing current in Amperes)
powerInput           = 0.0000,    // SYSTEM PARAMETER - Input power (solar power) in Watts
powerInputPrev       = 0.0000,    // SYSTEM PARAMETER - Previously stored input power variable for MPPT algorithm (Watts)
DeltaV               = 0.0000,    // SYSTEM PARAMETER -
DeltaI               = 0.0000,    // SYSTEM PARAMETER -
DeltaP               = 0.0000,    // SYSTEM PARAMETER -
DeltaD               = 0.0000,    // SYSTEM PARAMETER -
daysRunning          = 0.0000,    // SYSTEM PARAMETER - Stores the total number of days the MPPT device has been running since last powered
Hours                = 0.0000,    // SYSTEM PARAMETER - Used for reseting daily production
ProductionTodayWh    = 0.0000,    // SYSTEM PARAMETER - Stores the daily production in Wh
ProductionTodaykWh   = 0.0000,    // SYSTEM PARAMETER - Stores the daily production in kWh
Wh                   = 0.0000,    // SYSTEM PARAMETER - Stores the accumulated energy harvested (Watt-Hours)
kWh                  = 0.0000,    // SYSTEM PARAMETER - Stores the accumulated energy harvested (Kiliowatt-Hours)
MWh                  = 0.0000,    // SYSTEM PARAMETER - Stores the accumulated energy harvested (Megawatt-Hours)
loopTime             = 0.0000;    // SYSTEM PARAMETER -
int64_t
currentOLED_micros       = 0,     // SYSTEM PARAMETER -
currentReconnect_micros  = 0,     // SYSTEM PARAMETER -
currentRoutine_micros    = 0,     // SYSTEM PARAMETER -
prevOLED_micros          = 0,     // SYSTEM PARAMETER -
prevOLED_Toggle_micros   = 0,     // SYSTEM PARAMETER -
prevReconnect_micros     = 0,     // SYSTEM PARAMETER -
prevRoutine_micros       = 0,     // SYSTEM PARAMETER -
prevHour_micros          = 0,     // SYSTEM PARAMETER - Used for daily kWh reset counter
timeOn                   = 0,     // SYSTEM PARAMETER -
loopTimeStart            = 0,     // SYSTEM PARAMETER - Used for the loop cycle stop watch, records the loop start time
loopTimeEnd              = 0;     // SYSTEM PARAMETER - Used for the loop cycle stop watch, records the loop end time
void setup() {
  //SERIAL INITIALIZATION  
  Serial.begin(500000);
  //GPIO PIN INITIALIZATION
  pinMode(BCCU, OUTPUT);
  pinMode(TS,INPUT); 
  ads.setGain(GAIN_TWO);                 // 2x gain   +/- 2.048V  1 bit = 1mV      0.0625mV
  ads.setDataRate(RATE_ADS1115_860SPS);  // ADS1115 Sampling rate
  Wire.begin(SDA, SCL);                  // I2C PINS INITIALIZATION
  Wire.setClock(400000);                 // Set I2C frequency to 400khz
  ads.begin();                           // ADC INITIALIZATION
  ledc_timer.bit_num = LEDC_TIMER_11_BIT;       // resolution of PWM duty
  ledc_timer.freq_hz = 39000;                   // frequency of PWM signal
  ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;  // timer mode
  ledc_timer.timer_num = LEDC_TIMER_0;          // timer index
  ledc_timer_config(&ledc_timer);
  ledc_channel.channel = LEDC_CHANNEL_0;
  ledc_channel.duty = 0;
  ledc_channel.gpio_num = PWM_PIN;
  ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
  ledc_channel.timer_sel = LEDC_TIMER_0;
  ledc_channel_config(&ledc_channel);
  ledc_timer_2.bit_num = LEDC_TIMER_11_BIT;       // resolution of PWM duty
  ledc_timer_2.freq_hz = 39000;                   // frequency of PWM signal
  ledc_timer_2.speed_mode = LEDC_LOW_SPEED_MODE;  // timer mode
  ledc_timer_2.timer_num = LEDC_TIMER_0;          // timer index
  ledc_timer_config(&ledc_timer_2);
  ledc_channel_2.channel = LEDC_CHANNEL_2;
  ledc_channel_2.duty = 0;
  ledc_channel_2.gpio_num = PWM_PIN_2;
  ledc_channel_2.speed_mode = LEDC_LOW_SPEED_MODE;
  ledc_channel_2.timer_sel = LEDC_TIMER_0;
  ledc_channel_2.hpoint = 1023;                   // 180 degree phase shift
  u8g2.begin();   //OLED INITIALIZATION
  u8g2.clearBuffer();    
  u8g2.setFont(u8g2_font_spleen8x16_mf);
  u8g2.drawStr(50, 30, "MPPT ");
  u8g2.drawStr(15, 45, "FIRMWARE-V1.0");
  u8g2.sendBuffer();
  delay(2500);    
}
void loop() {
  Read_Sensors();        //TAB#2 - Sensor data measurement and computation
  Device_Protection();   //TAB#3 - Fault detection algorithm  
  Charging_Algorithm();  //TAB#4 - Battery Charging Algorithm                    
  OLED_Menu();           //TAB#5 - OLED Menu     
  //////////// LOOP TIME STOPWATCH ////////////
  loopTimeStart = esp_timer_get_time();                             //Record Start Time
  loopTime = (loopTimeStart - loopTimeEnd) / 1000.000;  //Compute Loop Cycle Speed (mS)
  loopTimeEnd = esp_timer_get_time();                               //Record End Time
  //Serial.print(" LoopT:"); Serial.print(loopTime,3);Serial.println("ms");
  Serial.print(voltageInput);
  Serial.print("V ");
  Serial.print(currentInput);
  Serial.print("A ");
  Serial.print(voltageOutput);
  Serial.print("V ");
  Serial.print(currentOutput);
  Serial.print("A ");
  Serial.print(loopTime);
  Serial.println("ms ");
}
//OLED Menu
void OLED_Menu() {
  currentOLED_micros = esp_timer_get_time();
  if (currentOLED_micros - prevOLED_micros >= OLED_Update_micros ) {
   u8g2.clearBuffer();                  // clear the internal memory
   if(screen == 0){screen_1();}
   else if(screen == 1){screen_2();}
   else if(screen == 2){screen_3();}
   else if(screen == 3){screen_4();}
   u8g2.sendBuffer();                     // transfer internal memory to the display
   prevOLED_micros = currentOLED_micros;
  }
  //Toggle screen
  if (currentOLED_micros - prevOLED_Toggle_micros >= OLED_Toggle_micros) {
   screen++;
   if(screen > 3){screen = 0;}
   prevOLED_Toggle_micros = currentOLED_micros;
  }
}
void screen_1(){ //Screen No:1, Print PV(solar) Voltage and Current
   u8g2.setFont(u8g2_font_spleen8x16_mf);
  u8g2.setCursor(58, 12); u8g2.print("PV");
  u8g2.drawFrame(2, 0, 124, 15);
  u8g2.setCursor(5, 35);  u8g2.print("Voltage:");
  u8g2.setCursor(75, 35); u8g2.print(voltageInput,1);  u8g2.println("V");
  u8g2.setCursor(5, 55);  u8g2.print("Current:");
  u8g2.setCursor(75, 55); u8g2.print(currentInput,1);  u8g2.println("A");
}
void screen_2(){ //Screen No:2, Print Battery Voltage and Current
   u8g2.setFont(u8g2_font_spleen8x16_mf);
  u8g2.setCursor(56, 12); u8g2.print("BAT");
  u8g2.drawFrame(2, 0, 124, 15);
  u8g2.setCursor(5, 35);  u8g2.print("Voltage:");
  u8g2.setCursor(75, 35); u8g2.print(voltageOutput,1);  u8g2.println("V");
  u8g2.setCursor(5, 55);  u8g2.print("Current:");
  u8g2.setCursor(75, 55); u8g2.print(currentOutput,1);  u8g2.println("A");
}
void screen_3(){ //Screen No:3, Print input power 
  u8g2.setFont(u8g2_font_spleen8x16_mf);
  u8g2.setCursor(46, 12); u8g2.print("POWER");
  u8g2.drawFrame(2, 0, 124, 15);
  u8g2.setCursor(15, 42); u8g2.print("Watts:");
  u8g2.setCursor(65, 42); u8g2.print(powerInput,1);  u8g2.println("W");
}
void screen_4(){ //Screen No:4, Print daily and total production
  u8g2.setFont(u8g2_font_spleen8x16_mf);
  u8g2.setCursor(25, 12); u8g2.print("PRODUCTION");
  u8g2.drawFrame(2, 0, 124, 15);
  u8g2.setCursor(10, 35); u8g2.print("Today:");
  u8g2.setCursor(60, 35); 
  if(ProductionTodayWh<10){u8g2.print(ProductionTodayWh,2);         u8g2.println("Wh");}
  else if(ProductionTodayWh<100){u8g2.print(ProductionTodayWh,1);    u8g2.println("Wh");}
  else if(ProductionTodayWh<10000){u8g2.print(ProductionTodaykWh,2);  u8g2.println("kWh");}
  else if(ProductionTodayWh<100000){u8g2.print(ProductionTodaykWh,1);  u8g2.println("kWh");}
  u8g2.setCursor(10, 55); u8g2.print("Total:");
  u8g2.setCursor(60, 55); 
  if(Wh<10){u8g2.print(Wh,2);         u8g2.println("Wh");}
  else if(Wh<100){u8g2.print(Wh,1);    u8g2.println("Wh");}
  else if(Wh<10000){u8g2.print(kWh,2);  u8g2.println("kWh");}
  else if(Wh<100000){u8g2.print(kWh,1);  u8g2.println("kWh");}
  else if(Wh<1000000){u8g2.print(kWh,0);  u8g2.println("kWh");}     
  else if(Wh<10000000){u8g2.print(MWh,1);  u8g2.println("MWh");}    
  else if(Wh<100000000){u8g2.print(MWh,0);  u8g2.println("MWh");}  
  else if(Wh<1000000000){u8g2.print(MWh,0);  u8g2.println("MWh");}  
}
//Charging algorithum
void Charging_Algorithm() {
  dutycycle = constrain(dutycycle, 0, 2047); //constrain dutycycle 
   if(Buck_Enable == 1){
    if(voltageOutput>BatteryVoltageMax)            {dutycycle-= 1;}            //  Voltage Is Above → Decrease Duty Cycle  
    else if(currentOutput > BatteryCurrentMax)     {dutycycle-= 0.1;}          //  Current Is Above → Decrease Duty Cycle             
     else{                                                                     //    MPPT ALGORITHM
          if(DeltaP > 0.0000 && DeltaV > 0.0000)             {dutycycle-=1;}   //  ↑P ↑V ; →MPP  //D--
          else if(DeltaP > 0.0000 && DeltaV < 0.0000)        {dutycycle+=1;}   //  ↑P ↓V ; MPP←  //D++
          else if(DeltaP < 0.0000 && DeltaV > 0.0000)        {dutycycle+=1;}   //  ↓P ↑V ; MPP→  //D++
          else if(DeltaP < 0.0000 && DeltaV < 0.0000)        {dutycycle-=1;}   //  ↓P ↓V ; ←MPP  //D--
          else if(voltageOutput<BatteryVoltageMax) {dutycycle+=1;}             //  MP MV ; MPP Reached -
          powerInputPrev   = powerInput;                                       //  Store Previous Recorded Power
          voltageInputPrev = voltageInput;                                     //  Store Previous Recorded Voltage
        }
   }
  ledcWrite(0, dutycycle); //Write PWM duty cycle to channel_0
  ledcWrite(2, dutycycle); //Write PWM duty cycle to channel_2
}
//device protection
void backflowControl() {
  unsigned long currentReconnect_micros = esp_timer_get_time();
  //If any anomaly happens disconnect the buck immediately and if everything is normal reconnect buck after some delay
  if (BNC == 1 || IUV == 1 || IOV == 1 || IOC == 1 || OOV == 1 || OTE == 1) {
    buck_Disable();
    prevReconnect_micros = currentReconnect_micros;
  } else if (voltageInput >= PV_VoltageMin && currentReconnect_micros - prevReconnect_micros >= ReconnectTime_micros) {
    buck_Enable();
  }
}
void buck_Enable() {     //Enable BUCK
  digitalWrite(BCCU, 1);
  Buck_Enable = 1;
}
void buck_Disable() {    //Disable BUCK
  digitalWrite(BCCU, 0);
  Buck_Enable   = 0;
  dutycycle     = 0;
  currentInput  = 0;
  currentOutput = 0;
  powerInput    = 0;
}
void Device_Protection() {
  if(voltageOutput < voltageBatteryMin)                       {BNC = 1;} else{BNC = 0;} //BNC - Battery Not Connected
  if(voltageInput < voltageOutput + voltageDropout)           {IUV = 1;} else{IUV = 0;} //IUV - Input Under Voltage
  if(voltageInput > PV_VoltageMax)                            {IOV = 1;} else{IOV = 0;} //IOV - Input Over Voltage
  if(currentInput > PV_CurrentMax)                            {IOC = 1;} else{IOC = 0;} //IOC - Input Over Current
  if(voltageOutput > BatteryVoltageMax + voltageBatteryThresh){OOV = 1;} else{OOV = 0;} //OOV - Output Over Voltage 
  if(temperature>temperatureMax)                              {OTE = 1;} else{OTE = 0;} //OTE - OVERTEMPERATURE: System overheat detected
  backflowControl();
}
//Read Sesnors
void Read_Sensors() {
    /////////// TEMPERATURE SENSOR /////////////
  if(sampleStoreTS<=avgCountTS){                               //TEMPERATURE SENSOR - Lite Averaging
    TS = TS + analogRead(TempSensor);
    sampleStoreTS++;   
  }
  else{
    TS = TS/sampleStoreTS;
    TSlog = log(ntcResistance*(4095.00/TS-1.00));
    temperature = (1.0/(1.009249522e-03+2.378405444e-04*TSlog+2.019202697e-07*TSlog*TSlog*TSlog))-273.15;
    sampleStoreTS = 0;
    TS = 0;
  }
  //FILTERING
  VSI = filter3.addSample(ads.computeVolts(ads.readADC_SingleEnded(3)));
  VSO = filter1.addSample(ads.computeVolts(ads.readADC_SingleEnded(1)));
  CSI = filter2.addSample(ads.computeVolts(ads.readADC_SingleEnded(2)));
  //VOLTAGE SENSOR
  voltageInput = VSI * inVoltageDivRatio;
  voltageOutput = VSO * outVoltageDivRatio;
  //CURRENT SENSOR
  currentInput = ((CSI - currentMidPoint) * -1) / currentSensV;
  if (currentInput < 0) { currentInput = 0.0000; }
  if (voltageOutput <= 0) { currentOutput = 0.0000; }
  else {currentOutput = (voltageInput*currentInput)/voltageOutput*efficiencyRate;}
  //POWER COMPUTATION - Through computation
  powerInput = voltageInput * currentInput;
  DeltaV = voltageInput - voltageInputPrev;
  DeltaI = currentInput - currentInputPrev;
  DeltaP = powerInput - powerInputPrev;
  //TIME DEPENDENT SENSOR DATA COMPUTATION
  currentRoutine_micros = esp_timer_get_time();  
  if(currentRoutine_micros - prevRoutine_micros >= RoutineInterval_micros){   //Run routine every millisRoutineInterval (ms)
    prevRoutine_micros = currentRoutine_micros;                               //Store previous time
    if(Buck_Enable == 1){                                                
      Wh = Wh+(powerInput/(3600.000*(1000000.000/RoutineInterval_micros)));   //Accumulate and compute energy harvested (3600s*(1000/interval))
      ProductionTodayWh = ProductionTodayWh+(powerInput/(3600.000*(1000000.000/RoutineInterval_micros)));
    }
    kWh = Wh/1000.000;
    MWh = Wh/1000000.000;
    ProductionTodaykWh = ProductionTodayWh/1000.000;
    daysRunning = timeOn/(86400.000*(1000000.000/RoutineInterval_micros));    //Compute for days running (86400s*(1000/interval))
    timeOn++;                                                                 //Increment time counter
  } 
  //Reset production daily. If there is no solar presense for more than 10 hours, Reset production.
  if(IUV == 1){
    Hours = ((currentRoutine_micros - prevHour_micros) / 3600000000.000);
  }
  if(IUV == 0){
    prevHour_micros = currentRoutine_micros;
    if(Hours >= 10){ProductionTodayWh = 0; Hours = 0;}
  }
}

Credits

VAIBHAV DUBEY
3 projects • 0 followers

Comments