/* =========================================================
AXPERT-STYLE HYBRID INVERTER + MPPT (Arduino Nano)
LiFePO4 | MPPT | UPS | Inverter | 2004 LCD
========================================================= */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
/* ------------------- PIN MAP ------------------- */
#define MPPT_PWM_PIN 9
//#define INV_AH_PIN 5
//#define INV_AL_PIN 6
//#define INV_BH_PIN 10
//#define INV_BL_PIN 11
#define SOLAR_V_PIN A0
#define SOLAR_I_PIN A1
#define BATT_V_PIN A2
#define BATT_I_PIN A3
#define AC_DETECT_PIN 2
#define BYPASS_RELAY 3
/* ------------------- LCD ------------------- */
LiquidCrystal_I2C lcd(0x27, 20, 4);
/* ------------------- PWM ------------------- */
#define PWM_MAX 480
#define PWM_MIN 30
uint16_t mpptPWM = 150;
/* ------------------- BATTERY ------------------- */
float battV, battI;
uint8_t battSystem = 48;
struct BatteryProfile {
float bulk;
float absorb;
float flt;
float maxI;
} batt;
/* ------------------- MPPT ------------------- */
float pvV, pvI, pvPrevV, pvPrevI;
/* ------------------- STATES ------------------- */
enum ChargeState { BULK, ABSORB, FLOAT };
ChargeState chgState = BULK;
/* ------------------- TIMERS ------------------- */
unsigned long tMPPT, tCHG, tUPS, tUI;
/* =========================================================
HARDWARE PWM (25kHz)
========================================================= */
void setupMPPTPWM() {
pinMode(MPPT_PWM_PIN, OUTPUT);
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (1 << COM1A1) | (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10);
ICR1 = PWM_MAX;
OCR1A = mpptPWM;
}
/* =========================================================
SENSOR READ (SCALES MUST BE CALIBRATED)
========================================================= */
void readSensors() {
pvV = analogRead(SOLAR_V_PIN) * 0.10;
pvI = analogRead(SOLAR_I_PIN) * 0.05;
battV = analogRead(BATT_V_PIN) * 0.05;
battI = analogRead(BATT_I_PIN) * 0.05;
}
/* =========================================================
BATTERY AUTO DETECT (LiFePO4)
========================================================= */
void detectBattery() {
if (battV < 15) battSystem = 12;
else if (battV < 30) battSystem = 24;
else battSystem = 48;
if (battSystem == 12)
batt = {14.2, 14.2, 13.6, 50};
else if (battSystem == 24)
batt = {28.4, 28.4, 27.2, 50};
else
batt = {56.8, 56.8, 54.4, 50};
}
/* =========================================================
INCREMENTAL CONDUCTANCE MPPT
========================================================= */
void taskMPPT() {
float dV = pvV - pvPrevV;
float dI = pvI - pvPrevI;
if (dV != 0) {
float slope = dI / dV;
float ref = -pvI / pvV;
if (slope > ref) mpptPWM += 2;
else mpptPWM -= 2;
}
mpptPWM = constrain(mpptPWM, PWM_MIN, PWM_MAX);
OCR1A = mpptPWM;
pvPrevV = pvV;
pvPrevI = pvI;
}
/* =========================================================
CHARGER CONTROL (AXPERT STYLE)
========================================================= */
void taskCharger() {
if (battI > batt.maxI) mpptPWM -= 5;
switch (chgState) {
case BULK:
if (battV >= batt.bulk) chgState = ABSORB;
break;
case ABSORB:
if (battV > batt.absorb) mpptPWM--;
if (battI < 0.05) chgState = FLOAT;
break;
case FLOAT:
if (battV < batt.flt) chgState = BULK;
break;
}
mpptPWM = constrain(mpptPWM, PWM_MIN, PWM_MAX);
OCR1A = mpptPWM;
}
/* =========================================================
UPS / GRID CONTROL
========================================================= */
void taskUPS() {
bool grid = digitalRead(AC_DETECT_PIN);
digitalWrite(BYPASS_RELAY, grid ? HIGH : LOW);
}
/* =========================================================
LCD UI
========================================================= */
void taskUI() {
lcd.setCursor(0,0);
lcd.print("PV: ");
lcd.print(pvV); lcd.print("V ");
lcd.print(pvI); lcd.print("A ");
lcd.setCursor(0,1);
lcd.print("BAT: ");
lcd.print(battV); lcd.print("V ");
lcd.print(battI); lcd.print("A ");
lcd.setCursor(0,2);
lcd.print("PWM: ");
lcd.print(mpptPWM);
lcd.print(" SYS: ");
lcd.print(battSystem);
lcd.print("V ");
lcd.setCursor(0,3);
lcd.print("CHG: ");
lcd.print(chgState == BULK ? "BULK " :
chgState == ABSORB ? "ABS " : "FLOAT");
}
/* =========================================================
SETUP
========================================================= */
void setup() {
pinMode(AC_DETECT_PIN, INPUT);
pinMode(BYPASS_RELAY, OUTPUT);
lcd.init();
lcd.backlight();
setupMPPTPWM();
delay(1000);
readSensors();
detectBattery();
}
/* =========================================================
MAIN LOOP (COOPERATIVE MULTITASKING)
========================================================= */
void loop() {
if (millis() - tMPPT >= 30) {
tMPPT = millis();
readSensors();
taskMPPT();
}
if (millis() - tCHG >= 10) {
tCHG = millis();
taskCharger();
}
if (millis() - tUPS >= 5) {
tUPS = millis();
taskUPS();
}
if (millis() - tUI >= 200) {
tUI = millis();
taskUI();
}
}
Batt
Solar
S Current
B Current