#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// LCD Configuration
LiquidCrystal_I2C lcd(0x27, 20, 4); // 20x4 LCD
// Pin Definitions
// H-Bridge Pins
const int H_BRIDGE_LOW1 = 2;
const int H_BRIDGE_HIGH1 = 3;
const int H_BRIDGE_LOW2 = 4;
const int H_BRIDGE_HIGH2 = 5;
// Control Pins
const int RELAY_PIN = 6;
const int FAN_PIN = 7;
const int POWER_BUTTON = 8;
// Sensor Pins
const int BATT_SENSOR = A0;
const int AC_SENSOR = A1;
const int NTC_SENSOR = A2;
const int CT_SENSOR = A3;
// SPWM Configuration
const int SPWM_FREQ = 50; // Hz
const int CARRIER_FREQ = 23400; // Hz
const int SPWM_STEPS = 256;
const int SPWM_ARRAY_SIZE = 360;
// SPWM sine table
uint8_t sineTable[SPWM_ARRAY_SIZE];
unsigned long carrierPeriod;
unsigned long spwmPeriod;
// System Variables
float batteryVoltage = 0.0;
float acVoltage = 0.0;
float temperature = 0.0;
float loadCurrent = 0.0;
float loadPercent = 0.0;
float acFrequency = 0.0;
// Charger States
enum ChargerState {
CHARGER_OFF,
BULK_CHARGE,
ABSORPTION_CHARGE,
FLOAT_CHARGE
};
ChargerState chargerState = CHARGER_OFF;
// System States
enum SystemState {
SYSTEM_OFF,
UPS_MODE,
INVERTER_MODE,
CHARGER_MODE
};
SystemState systemState = SYSTEM_OFF;
// Timing Variables
unsigned long lastSPWMUpdate = 0;
unsigned long lastDisplayUpdate = 0;
unsigned long lastSensorUpdate = 0;
unsigned long lastACCheck = 0;
unsigned long chargeStartTime = 0;
// Charger Parameters
const float BULK_VOLTAGE = 14.4; // 12V battery bulk charge voltage
const float ABSORPTION_VOLTAGE = 13.8; // Absorption voltage
const float FLOAT_VOLTAGE = 13.2; // Float voltage
const float BATTERY_LOW = 11.0; // Low battery cutoff
const float BATTERY_HIGH = 14.6; // High battery cutoff
// Calibration factors
const float BATT_CALIBRATION = 0.0244; // Adjust based on voltage divider
const float AC_CALIBRATION = 0.5; // Adjust for AC sensor
const float CT_CALIBRATION = 0.1; // Adjust for current transformer
void setup() {
// Initialize pins
pinMode(H_BRIDGE_LOW1, OUTPUT);
pinMode(H_BRIDGE_HIGH1, OUTPUT);
pinMode(H_BRIDGE_LOW2, OUTPUT);
pinMode(H_BRIDGE_HIGH2, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
pinMode(POWER_BUTTON, INPUT_PULLUP);
// Initialize H-Bridge to safe state
digitalWrite(H_BRIDGE_LOW1, LOW);
digitalWrite(H_BRIDGE_HIGH1, LOW);
digitalWrite(H_BRIDGE_LOW2, LOW);
digitalWrite(H_BRIDGE_HIGH2, LOW);
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.clear();
// Generate SPWM sine table
generateSineTable();
// Calculate periods
carrierPeriod = 1000000 / CARRIER_FREQ; // microseconds
spwmPeriod = 1000000 / SPWM_FREQ; // microseconds
// Initialize serial for debugging
Serial.begin(9600);
// Display startup message
lcd.setCursor(0, 0);
lcd.print(" UPS INVERTER ");
lcd.setCursor(0, 1);
lcd.print(" SYSTEM V1.0 ");
delay(2000);
lcd.clear();
}
void loop() {
unsigned long currentTime = micros();
// Read sensors every 100ms
if (currentTime - lastSensorUpdate >= 100000) {
readSensors();
lastSensorUpdate = currentTime;
}
// Update display every 500ms
if (currentTime - lastDisplayUpdate >= 500000) {
updateDisplay();
lastDisplayUpdate = currentTime;
}
// Check power button
checkPowerButton();
// Main state machine
switch(systemState) {
case SYSTEM_OFF:
handleSystemOff();
break;
case UPS_MODE:
handleUPSMode();
break;
case INVERTER_MODE:
handleInverterMode();
break;
case CHARGER_MODE:
handleChargerMode();
break;
}
// Generate SPWM if inverter is active
if (systemState == INVERTER_MODE) {
generateSPWM();
}
}
void generateSineTable() {
for (int i = 0; i < SPWM_ARRAY_SIZE; i++) {
float angle = (i * 2 * PI) / SPWM_ARRAY_SIZE;
sineTable[i] = (sin(angle) + 1.0) * (SPWM_STEPS - 1) / 2.0;
}
}
void generateSPWM() {
static unsigned long lastCarrierTime = 0;
static unsigned long lastSPWMStep = 0;
static int currentStep = 0;
unsigned long currentTime = micros();
// Update SPWM step at 50Hz
if (currentTime - lastSPWMStep >= spwmPeriod / SPWM_ARRAY_SIZE) {
currentStep = (currentStep + 1) % SPWM_ARRAY_SIZE;
lastSPWMStep = currentTime;
}
// Carrier frequency switching at 23.4kHz
if (currentTime - lastCarrierTime >= carrierPeriod) {
uint8_t dutyCycle = sineTable[currentStep];
// Generate PWM for H-Bridge (simplified - adjust based on your H-bridge driver)
// This is a basic implementation - you may need to add dead time
if (dutyCycle > 127) {
digitalWrite(H_BRIDGE_HIGH1, HIGH);
digitalWrite(H_BRIDGE_LOW2, HIGH);
digitalWrite(H_BRIDGE_LOW1, LOW);
digitalWrite(H_BRIDGE_HIGH2, LOW);
} else {
digitalWrite(H_BRIDGE_HIGH1, LOW);
digitalWrite(H_BRIDGE_LOW2, LOW);
digitalWrite(H_BRIDGE_LOW1, HIGH);
digitalWrite(H_BRIDGE_HIGH2, HIGH);
}
lastCarrierTime = currentTime;
}
}
void readSensors() {
// Read battery voltage
int battRaw = analogRead(BATT_SENSOR);
batteryVoltage = battRaw * BATT_CALIBRATION;
// Read AC voltage
int acRaw = analogRead(AC_SENSOR);
acVoltage = abs(acRaw - 512) * AC_CALIBRATION; // Assuming AC centered around 2.5V
// Read temperature
int ntcRaw = analogRead(NTC_SENSOR);
temperature = readTemperature(ntcRaw);
// Read load current
int ctRaw = analogRead(CT_SENSOR);
loadCurrent = abs(ctRaw - 512) * CT_CALIBRATION;
loadPercent = (loadCurrent / 10.0) * 100; // Assuming 10A max current
// Calculate AC frequency (simplified)
acFrequency = calculateACFrequency();
// Control fan based on temperature
if (temperature > 40.0) {
digitalWrite(FAN_PIN, HIGH);
} else if (temperature < 35.0) {
digitalWrite(FAN_PIN, LOW);
}
}
float readTemperature(int rawValue) {
// Convert NTC reading to temperature
// This is a simplified calculation - calibrate for your specific NTC
float resistance = 10000.0 / (1023.0 / rawValue - 1.0);
float steinhart = resistance / 10000.0;
steinhart = log(steinhart);
steinhart /= 3950.0;
steinhart += 1.0 / (25.0 + 273.15);
steinhart = 1.0 / steinhart;
steinhart -= 273.15;
return steinhart;
}
float calculateACFrequency() {
// Simplified AC frequency measurement
// You may want to implement zero-crossing detection for better accuracy
static unsigned long lastZeroCross = 0;
static float frequency = 50.0;
// This is a placeholder - implement proper zero-crossing detection
if (acVoltage > 100 && millis() - lastZeroCross > 10) {
frequency = 1000.0 / (millis() - lastZeroCross) * 0.5;
lastZeroCross = millis();
}
return constrain(frequency, 45.0, 55.0);
}
void checkPowerButton() {
static unsigned long lastButtonPress = 0;
if (digitalRead(POWER_BUTTON) == LOW) {
if (millis() - lastButtonPress > 500) { // Debounce
togglePower();
lastButtonPress = millis();
}
}
}
void togglePower() {
if (systemState == SYSTEM_OFF) {
// Turn system on
if (acVoltage >= 200 && acVoltage <= 235) {
systemState = UPS_MODE;
digitalWrite(RELAY_PIN, HIGH); // Switch to UPS mode
} else {
systemState = INVERTER_MODE;
digitalWrite(RELAY_PIN, LOW); // Switch to inverter mode
}
} else {
// Turn system off
systemState = SYSTEM_OFF;
chargerState = CHARGER_OFF;
digitalWrite(RELAY_PIN, LOW);
// Turn off H-Bridge
digitalWrite(H_BRIDGE_LOW1, LOW);
digitalWrite(H_BRIDGE_HIGH1, LOW);
digitalWrite(H_BRIDGE_LOW2, LOW);
digitalWrite(H_BRIDGE_HIGH2, LOW);
}
}
void handleSystemOff() {
// System is off, do nothing
}
void handleUPSMode() {
// AC power is available, charge battery if needed
if (batteryVoltage < FLOAT_VOLTAGE) {
systemState = CHARGER_MODE;
}
// Monitor AC power
if (acVoltage < 200 || acVoltage > 235) {
systemState = INVERTER_MODE;
digitalWrite(RELAY_PIN, LOW);
}
}
void handleInverterMode() {
// Check battery protection
if (batteryVoltage < BATTERY_LOW) {
systemState = SYSTEM_OFF;
digitalWrite(RELAY_PIN, LOW);
return;
}
// Check if AC power returns
if (acVoltage >= 200 && acVoltage <= 235) {
systemState = UPS_MODE;
digitalWrite(RELAY_PIN, HIGH);
}
}
void handleChargerMode() {
// Smart charger state machine
switch(chargerState) {
case CHARGER_OFF:
if (batteryVoltage < BULK_VOLTAGE) {
chargerState = BULK_CHARGE;
chargeStartTime = millis();
}
break;
case BULK_CHARGE:
// Constant current charge
if (batteryVoltage >= BULK_VOLTAGE) {
chargerState = ABSORPTION_CHARGE;
chargeStartTime = millis();
}
break;
case ABSORPTION_CHARGE:
// Constant voltage charge
if (millis() - chargeStartTime > 7200000) { // 2 hours absorption
chargerState = FLOAT_CHARGE;
} else if (batteryVoltage < ABSORPTION_VOLTAGE - 0.5) {
chargerState = BULK_CHARGE;
}
break;
case FLOAT_CHARGE:
// Maintenance charge
if (batteryVoltage < FLOAT_VOLTAGE - 0.2) {
chargerState = BULK_CHARGE;
}
break;
}
// Implement charger control here
// This would control your charger circuitry
// Check if charging should stop
if (batteryVoltage >= FLOAT_VOLTAGE && chargerState == FLOAT_CHARGE) {
systemState = UPS_MODE;
chargerState = CHARGER_OFF;
}
}
void updateDisplay() {
lcd.setCursor(0, 0);
lcd.print("Batt:");
lcd.print(batteryVoltage, 1);
lcd.print("V ");
lcd.print("Temp:");
lcd.print(temperature, 0);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("AC In:");
lcd.print(acVoltage, 0);
lcd.print("V ");
lcd.print(acFrequency, 1);
lcd.print("Hz");
lcd.setCursor(0, 2);
lcd.print("Load:");
lcd.print(loadPercent, 0);
lcd.print("% ");
// Display system state
lcd.setCursor(0, 3);
switch(systemState) {
case SYSTEM_OFF: lcd.print("SYSTEM OFF "); break;
case UPS_MODE: lcd.print("UPS MODE "); break;
case INVERTER_MODE: lcd.print("INVERTER MODE "); break;
case CHARGER_MODE:
lcd.print("CHARGER:");
switch(chargerState) {
case BULK_CHARGE: lcd.print("BULK "); break;
case ABSORPTION_CHARGE: lcd.print("ABSORPT "); break;
case FLOAT_CHARGE: lcd.print("FLOAT "); break;
default: lcd.print("OFF "); break;
}
break;
}
}
// Emergency shutdown for overvoltage/overcurrent
void emergencyShutdown() {
systemState = SYSTEM_OFF;
chargerState = CHARGER_OFF;
digitalWrite(H_BRIDGE_LOW1, LOW);
digitalWrite(H_BRIDGE_HIGH1, LOW);
digitalWrite(H_BRIDGE_LOW2, LOW);
digitalWrite(H_BRIDGE_HIGH2, LOW);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(FAN_PIN, HIGH); // Keep fan on for cooling
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("EMERGENCY SHUTDOWN");
lcd.setCursor(0, 1);
lcd.print("CHECK SYSTEM ");
while(true) {
// Wait for reset
}
}