|
KiCADKicad
|
|
|
LT Spice |
|
|
arduino IDEArduino
|
500w power station pcb
Title:
500W Microcontroller-Based Solar & Mains Hybrid Power Station PCB with MPPT Charging
What’s the project about?
This project is a compact, all-in-one hybrid power station PCB designed to deliver clean, reliable power from multiple sources — mains electricity, solar panels, or an external DC supply.
It integrates a 500W pure sine wave inverter, a 35A MPPT solar charge controller, and multiple USB outputs, making it perfect for portable power systems, off-grid applications, or backup power setups.
Why did I decide to make it?
In many regions, power supply is unstable or unavailable for extended periods. Most available portable power stations are either too expensive or lack important features like solar charging and high-current outputs.
I wanted to create a cost-effective, feature-packed PCB that could be manufactured and assembled anywhere, allowing more people to build their own reliable, renewable power solution.
How does it work?
Solar & Mains Charging:
Built-in 35A MPPT charge controller ensures efficient charging from 12V solar panels.
AC mains charging option for versatility.
Pure Sine Wave Inverter (500W):
Converts stored DC energy into stable AC output, safe for sensitive electronics.
USB Outputs:
5 × USB ports, each providing 5V at 3A for fast charging of devices.
Smart Protection Features:
Overvoltage, undervoltage, overcurrent, and low-battery protection.
Microcontroller Control:
Coordinates charging, power delivery, and protection features for optimal efficiency.
Power Sources Supported:
12V battery input
12V solar panel
13–24V external power supply
Specifications:
MPPT Controller: 12V, 35A
Inverter Output: 500W, pure sine wave
USB Output: 5 × 5V/3A
Battery: 12V (lead-acid or LiFePO₄ compatible)
Input Range: 13–24V DC
Protection: Over/under voltage, overcurrent, low battery cutoff
// --- LIBRARIES ---
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- PIN DEFINITIONS ---
const int solarVoltagePin = A3; // Solar input voltage via divider (to check for sufficient sunlight)
const int inputVoltagePin = A0; // General input voltage for MPPT logic
const int batteryCurrentPin = A1; // Charging current via ACS712 sensor
const int batteryVoltagePin = A2; // Battery voltage via divider
const int mpptControlPin = 6; // PWM pin to control the IR2104 input
const int ir2104ShutdownPin = 5; // IR2104 shutdown pin (LOW to enable)
const int solarRelayPin = 7; // Digital pin to control the solar input relay
const int buzzerPin = 8; // Buzzer for audible warnings
const int lowLedPin = 9; // Low battery status LED
const int mediumLedPin = 10; // Medium battery status LED
const int highLedPin = 11; // High battery status LED
const int relayControlPin = 4; // Relay control pin for battery cutoff
const int powerButtonPin = 3; // Power button to toggle main relay on D4
// --- I2C LCD ADDRESS (COMMON VALUES ARE 0x27, 0x3F) ---
LiquidCrystal_I2C lcd(0x27, 16, 2);
// --- VOLTAGE DIVIDER RESISTOR VALUES ---
// Both the solar and general input voltage use the same voltage divider circuit.
const float solarR1 = 100000.0; // 100k Ohm resistor connected to the + input
const float solarR2 = 20000.0; // 20k Ohm resistor connected to ground
// Battery Voltage Divider (10k to 20k)
const float batteryR1 = 10000.0; // 10k Ohm resistor connected to battery +
const float batteryR2 = 20000.0; // 20k Ohm resistor connected to ground
// --- ACS712 CURRENT SENSOR PARAMETERS ---
// ACS712 30A version has a sensitivity of 66mV/A.
// The output voltage is centered at VCC/2 (2.5V).
const float acs712_sensitivity = 0.066; // 66mV per Ampere
const float acs712_offset_voltage = 2.5; // Offset voltage (VCC / 2)
// --- VOLTAGE THRESHOLDS ---
// Values are in Volts for a 12V lead-acid battery and solar panel.
const float lowVoltageThreshold = 10.8; // Turn OFF relay below this voltage (10%)
const float warningVoltageThreshold = 11.2; // Start warning below this voltage (15%)
const float chargedVoltageThreshold = 12.0; // Re-activate relay when voltage rises above this
const float fullyChargedVoltage = 12.8; // Target voltage for a lead-acid battery
// Solar panel voltage threshold to turn on the solar relay.
// This prevents trying to charge in very low light conditions.
const float solarOnVoltageThreshold = 14.0; // Solar voltage must be above this to activate charging
const float powerInputBeepThreshold = 13.0; // Voltage on A0 must be above this to trigger the beep.
// --- MPPT ALGORITHM PARAMETERS ---
int pwmDutyCycle = 128; // Start with a middle PWM value (0-255)
int stepSize = 1; // How much to increase/decrease the duty cycle each loop
float previousPower = 0.0; // Variable to store the power from the previous loop
// --- TIMING VARIABLES ---
unsigned long lastBuzzerTime = 0;
const unsigned long buzzerInterval = 60000; // 60 seconds (1 minute)
unsigned long ledAnimationTime = 0;
const unsigned long ledAnimationInterval = 200; // 200ms for the loading animation
unsigned long buttonPressStartTime = 0;
const unsigned long longPressDuration = 4000; // 4 seconds for a long press
unsigned long powerOnDisplayTime = 0;
const unsigned long powerOnDisplayDuration = 3000; // 3 seconds to display "Power ON"
unsigned long lastPowerInputBeepTime = 0;
const unsigned long powerInputBeepInterval = 5000; // Beep every 5 seconds when power is detected
// --- SYSTEM STATE VARIABLES ---
bool isSystemOn = false; // Tracks if the main relay is enabled
bool isCharging = false; // Tracks if the system is actively charging
bool buttonPressed = false; // Tracks the state of the power button
bool showPowerOnMessage = false; // Flag to show the "Power ON" message on the LCD
// --- HELPER FUNCTIONS ---
// Reads the analog value from the solar panel voltage divider and converts it to voltage.
float readSolarVoltage() {
int analogValue = analogRead(solarVoltagePin);
float vIn = analogValue * (5.0 / 1023.0);
return vIn * ((solarR1 + solarR2) / solarR2);
}
// Reads the analog value from the input voltage divider and converts it to voltage.
float readInputVoltage() {
int analogValue = analogRead(inputVoltagePin);
float vIn = analogValue * (5.0 / 1023.0);
return vIn * ((solarR1 + solarR2) / solarR2);
}
// Reads the analog value from the battery voltage divider and converts it to voltage.
float readBatteryVoltage() {
int analogValue = analogRead(batteryVoltagePin);
float vIn = analogValue * (5.0 / 1023.0);
return vIn * ((batteryR1 + batteryR2) / batteryR2);
}
// Reads the analog value from the ACS712 current sensor and converts it to current.
float readSolarCurrent() {
int analogValue = analogRead(batteryCurrentPin);
float vOut = analogValue * (5.0 / 1023.0);
return (vOut - acs712_offset_voltage) / acs712_sensitivity;
}
// Calculates battery percentage based on voltage thresholds.
int batteryPercentage(float voltage) {
// Simple linear mapping from lowVoltageThreshold to fullyChargedVoltage
int percentage = map(voltage * 100, lowVoltageThreshold * 100, fullyChargedVoltage * 100, 0, 100);
return constrain(percentage, 0, 100);
}
// Controls the status LEDs based on a given state.
// 0 = Charged, 1 = Charging (or medium), 2 = Low
void updateLeds(int state) {
if (state == 0) { // Charged
digitalWrite(highLedPin, HIGH);
digitalWrite(mediumLedPin, LOW);
digitalWrite(lowLedPin, LOW);
} else if (state == 1) { // Charging / Medium
digitalWrite(highLedPin, LOW);
digitalWrite(mediumLedPin, HIGH);
digitalWrite(lowLedPin, LOW);
} else if (state == 2) { // Low
digitalWrite(highLedPin, LOW);
digitalWrite(mediumLedPin, LOW);
// Blink the low LED
if (millis() % 1000 < 500) {
digitalWrite(lowLedPin, HIGH);
} else {
digitalWrite(lowLedPin, LOW);
}
}
}
// Creates a loading animation with the LEDs
void loadingAnimation() {
if (millis() - ledAnimationTime >= ledAnimationInterval) {
static int currentLed = 0;
// Turn all LEDs off
digitalWrite(lowLedPin, LOW);
digitalWrite(mediumLedPin, LOW);
digitalWrite(highLedPin, LOW);
// Turn on the current LED
if (currentLed == 0) digitalWrite(lowLedPin, HIGH);
else if (currentLed == 1) digitalWrite(mediumLedPin, HIGH);
else if (currentLed == 2) digitalWrite(highLedPin, HIGH);
// Move to the next LED
currentLed = (currentLed + 1) % 3;
ledAnimationTime = millis();
}
}
// Handles the power button logic to turn the system on/off with a long press.
void handlePowerButton() {
// Read the button state (assuming a pull-down resistor, HIGH when pressed)
int buttonState = digitalRead(powerButtonPin);
if (buttonState == HIGH && !buttonPressed) {
// The button was just pressed
buttonPressed = true;
buttonPressStartTime = millis();
} else if (buttonState == HIGH && buttonPressed) {
// The button is being held down, check for a long press
if (millis() - buttonPressStartTime >= longPressDuration) {
// Long press detected, toggle the system state
isSystemOn = !isSystemOn;
buttonPressed = false; // Reset the state to prevent re-triggering
// Update the main relay pin immediately
if (isSystemOn) {
digitalWrite(relayControlPin, HIGH);
Serial.println("System ON via power button");
showPowerOnMessage = true;
powerOnDisplayTime = millis();
} else {
digitalWrite(relayControlPin, LOW);
Serial.println("System OFF via power button");
}
}
} else if (buttonState == LOW && buttonPressed) {
// The button was released before a long press was detected
buttonPressed = false;
}
}
// Updates the LCD display with system status.
void updateDisplay(float batteryVoltage, float solarCurrent) {
// If the system is not on, turn off the display.
if (!isSystemOn) {
lcd.noBacklight();
lcd.clear();
return;
}
// Turn on the backlight if the system is on.
lcd.backlight();
// Check if we need to show the "Power ON" message briefly.
if (showPowerOnMessage) {
lcd.setCursor(0, 0);
lcd.print(" POWER ON ");
lcd.setCursor(0, 1);
lcd.print(" ");
if (millis() - powerOnDisplayTime >= powerOnDisplayDuration) {
showPowerOnMessage = false;
lcd.clear(); // Clear the display after the message
}
return;
}
// --- Display Normal Status ---
// Line 1: Battery percentage and voltage
lcd.setCursor(0, 0);
lcd.print("Bat: ");
lcd.print(batteryPercentage(batteryVoltage));
lcd.print("% ");
lcd.print(batteryVoltage, 1); // Display voltage with one decimal place
lcd.print("V");
lcd.print(" "); // Clear remaining space
// Line 2: Charging state and mode
lcd.setCursor(0, 1);
lcd.print("Status: ");
if (solarCurrent > 0.1 && digitalRead(solarRelayPin) == HIGH) { // Check for charging (current > 0.1A to avoid noise)
lcd.print("Charging");
lcd.print(" ");
} else if (solarCurrent < -0.1) { // Discharging (current < -0.1A)
lcd.print("Dischrg");
lcd.print(" ");
} else {
lcd.print("Full");
lcd.print(" ");
}
// Display the mode (solar vs mains, using our solar relay as a proxy)
if (digitalRead(solarRelayPin) == HIGH) {
lcd.print("SOLAR ");
} else {
lcd.print("MAINS "); // Assuming an alternative power source when solar is off
}
}
// --- SETUP FUNCTION ---
void setup() {
Serial.begin(9600);
// Configure pin modes
pinMode(mpptControlPin, OUTPUT);
pinMode(ir2104ShutdownPin, OUTPUT);
pinMode(solarRelayPin, OUTPUT);
pinMode(relayControlPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(lowLedPin, OUTPUT);
pinMode(mediumLedPin, OUTPUT);
pinMode(highLedPin, OUTPUT);
pinMode(powerButtonPin, INPUT); // Configure button pin as an input
// Initialize the LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(" CONTROLLER ");
lcd.setCursor(0, 1);
lcd.print(" SYSTEM ");
delay(3000); // Display startup message for 3 seconds
lcd.clear();
// Set the shutdown pin to LOW to enable the IR2104 driver
digitalWrite(ir2104ShutdownPin, LOW);
// Initially, turn off all relays and feedback LEDs
digitalWrite(solarRelayPin, LOW); // Solar relay starts OFF
digitalWrite(relayControlPin, LOW);
digitalWrite(buzzerPin, LOW);
digitalWrite(lowLedPin, LOW);
digitalWrite(mediumLedPin, LOW);
digitalWrite(highLedPin, LOW);
// Hysteresis logic: check initial battery state
float initialBatteryVoltage = readBatteryVoltage();
if (initialBatteryVoltage >= chargedVoltageThreshold) {
isSystemOn = true;
digitalWrite(relayControlPin, HIGH); // Turn on the relay if battery is charged
}
}
// --- MAIN LOOP ---
void loop() {
// Read all sensor data
float solarVoltage = readSolarVoltage(); // Reads the solar panel voltage from A3
float inputVoltage = readInputVoltage(); // Reads the input voltage for MPPT from A0
float batteryVoltage = readBatteryVoltage();
float solarCurrent = readSolarCurrent();
// Handle the power button logic
handlePowerButton();
// --- POWER INPUT BUZZER LOGIC ---
// Beep to alert the user that a new power source has been detected.
if (inputVoltage > powerInputBeepThreshold && millis() - lastPowerInputBeepTime >= powerInputBeepInterval) {
tone(buzzerPin, 800);
delay(200);
noTone(buzzerPin);
lastPowerInputBeepTime = millis();
}
// --- SOLAR PANEL RELAY LOGIC ---
// Activate the solar relay (D7) only when the solar voltage is high enough.
if (solarVoltage >= solarOnVoltageThreshold) {
digitalWrite(solarRelayPin, HIGH);
} else {
digitalWrite(solarRelayPin, LOW);
}
// --- MPPT ALGORITHM & CHARGING LOGIC ---
// Only charge if the main system is ON, the solar relay is ON, and the battery isn't full.
isCharging = false;
if (isSystemOn && digitalRead(solarRelayPin) == HIGH && batteryVoltage < fullyChargedVoltage) {
isCharging = true;
// Calculate power using the input voltage and current
float solarPower = inputVoltage * solarCurrent;
// Perturb and Observe MPPT algorithm
if (solarPower > previousPower) {
pwmDutyCycle += stepSize;
} else {
stepSize = -stepSize;
pwmDutyCycle += stepSize;
}
pwmDutyCycle = constrain(pwmDutyCycle, 0, 255);
analogWrite(mpptControlPin, pwmDutyCycle);
previousPower = solarPower;
} else {
// Stop charging if conditions are not met
analogWrite(mpptControlPin, 0);
}
// --- ACTUATOR CONTROL AND FEEDBACK ---
// The relay is controlled by the `isSystemOn` flag.
if (isSystemOn) {
digitalWrite(relayControlPin, HIGH);
} else {
digitalWrite(relayControlPin, LOW);
}
// LED and buzzer logic
if (isSystemOn) {
if (batteryVoltage < warningVoltageThreshold) {
updateLeds(2); // Low battery warning (blinking low LED)
if (millis() - lastBuzzerTime >= buzzerInterval) {
tone(buzzerPin, 1000);
delay(200);
noTone(buzzerPin);
lastBuzzerTime = millis();
}
} else if (batteryVoltage < chargedVoltageThreshold) {
updateLeds(1); // Medium battery status
noTone(buzzerPin);
} else {
updateLeds(0); // Charged battery status
noTone(buzzerPin);
}
} else {
// If system is off, LEDs should be off unless charging
if (isCharging) {
loadingAnimation(); // Charging animation
} else {
digitalWrite(lowLedPin, LOW);
digitalWrite(mediumLedPin, LOW);
digitalWrite(highLedPin, LOW);
}
noTone(buzzerPin);
}
// --- DISPLAY UPDATES ---
updateDisplay(batteryVoltage, solarCurrent);
// Print status to Serial Monitor for debugging
Serial.print("Solar V (A3): "); Serial.print(solarVoltage);
Serial.print("V, Input V (A0): "); Serial.print(inputVoltage);
Serial.print("V, Solar Relay: "); Serial.print(digitalRead(solarRelayPin));
Serial.print(", Batt V: "); Serial.print(batteryVoltage);
Serial.print("V, System ON: "); Serial.println(isSystemOn ? "YES" : "NO");
delay(200); // Small delay to prevent flooding the Serial Monitor
}
500w power station pcb
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.
Raspberry Pi 5 7 Inch Touch Screen IPS 1024x600 HD LCD HDMI-compatible Display for RPI 4B 3B+ OPI 5 AIDA64 PC Secondary Screen(Without Speaker)
BUY NOW- Comments(0)
- Likes(2)
-
Engineer
Aug 23,2025
-
George Ituen
Aug 11,2025
- 0 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
More by George Ituen
-
A Compact Charging Breakout Board For Waveshare ESP32-C3
373 3 4 -
AI-driven LoRa & LLM-enabled Kiosk & Food Delivery System
391 2 0 -
-
-
-
ESP32-C3 BLE Keyboard - Battery Powered with USB-C Charging
596 0 1 -
-
mammoth-3D SLM Voron Toolhead – Manual Drill & Tap Edition
618 0 1 -
-
AEL-2011 Power Supply Module
1260 0 2 -
AEL-2011 50W Power Amplifier
1121 0 2







