Generator Control System - Master Controller for Raspberry Pi
7.2K
A working (in production in my system), distributed generator control system designed for off-grid solar installations with Victron energy systems. Built on a master-slave architecture using Raspberry Pi devices, it provides automated generator management with manual override capabilities, scheduling, and real-time monitoring through a modern web interface.
While designed around Victron integration, the architecture is universal—any two-wire trigger source (home automation, PLC, manual switch, or relay output) can signal GenMaster, and GenSlave can control any device with a two-wire start input (generators, pumps, HVAC, irrigation, industrial equipment). With optional Tailscale VPN and Cloudflare Tunnel support, your trigger source and controlled device can operate anywhere in the world—same building or different continents, wherever there's internet access.
The RPi Generator Control system automates generator management for off-grid solar installations. When your Victron Cerbo GX determines that battery levels are low and generator power is needed, it sends a signal to GenMaster, which coordinates with GenSlave to physically start the generator.

This is a system that starts and stops a real generator in a real location, sometimes unattended. Safety is treated as a first-class concern, not an afterthought — it shows up at every layer of the stack:

The EPO and HOA are optional — the system runs fine without either — but together they give you the hardware-enforced safety and operator-side control you'd expect from a production-grade generator setup. See Hardware Switches for the full operator guide and the Security model for why the two-layer pattern is the actual safety guarantee.
| Feature | Description |
|---|---|
| Victron Integration | Monitors GPIO signal from Cerbo GX relay for automated start/stop |
| Scheduled Runs | APScheduler-based scheduling with cron expressions |
| Manual Override | Force start/stop from web UI regardless of Victron signal |
| State Machine | Comprehensive state tracking (idle, starting, running, stopping, cooldown) |
| Feature | Description |
|---|---|
| Automation Arming | Explicit arm/disarm prevents accidental operations during startup or maintenance |
| Heartbeat System | Continuous health monitoring between GenMaster and GenSlave |
| Independent Failsafe | GenSlave automatically stops generator if communication lost for 30s |
| Hardware EPO (optional) | Schneider XB4 mushroom E-stop at the generator. NC contact physically interrupts the start circuit AND signals software — hardware-enforced maintenance lockout |
| HOA Selector (optional) | Schneider XB4 3-position rotary at the operator location. Quiet / Auto / Run modes for everyday operator control without touching the web UI |
| State Persistence | PostgreSQL database survives reboots and power failures |
| Webhook Notifications | Real-time alerts to n8n, Home Assistant, or any webhook receiver |
| Feature | Description |
|---|---|
| Real-time Dashboard | Live status updates via WebSocket |
| Dark Mode | Full dark/light theme support |
| Mobile Responsive | Works on any device |
| Container Management | Portainer integration for Docker control |
| Feature | Description |
|---|---|
| Generator Profile | Store manufacturer, model number, and serial number |
| Fuel Configuration | Configure fuel type (LPG, Natural Gas, Diesel) and expected load |
| Consumption Rates | Set fuel consumption rates at 50% and 100% load |
| Per-Run Tracking | Automatically track fuel usage for each generator run |
| Fuel History | View estimated fuel consumed in run history |
| Feature | Description |
|---|---|
| Automated Exercise | Schedule regular generator runs for maintenance |
| Configurable Frequency | Set exercise interval (weekly, bi-weekly, monthly, or custom) |
| Time Selection | Choose start time for exercise runs |
| Duration Control | Set how long each exercise run should last |
| Run Now | Manually trigger an exercise run on demand |
| Feature | Description |
|---|---|
| Run History | Complete log of all generator runs with duration, trigger, and fuel usage |
| Statistics | Daily, monthly, and all-time runtime tracking |
| System Health | CPU, memory, disk, and temperature monitoring |
| Backup/Restore | Database backup with one-click restore |

| Component | Technology | Purpose |
|---|---|---|
| GenMaster Backend | FastAPI + Python 3.11 | REST API, state machine, scheduler |
| GenMaster Frontend | Vue.js 3 + Tailwind CSS | Reactive web interface |
| Database | PostgreSQL 16 | State persistence, run history, configuration |
| Cache | Redis | Session storage, real-time data |
| Reverse Proxy | Nginx | HTTPS termination, rate limiting, security headers |
| GenSlave Backend | FastAPI (Docker) | Relay control, heartbeat responder |
| HAT | Pimoroni Automation Hat Mini | Physical relay + LCD status display |
| Component | Specification |
|---|---|
| Computer | Raspberry Pi 5 (8GB RAM) |
| Storage | 128GB NVMe SSD via PCIe adapter |
| Power | 5V 5A USB-C supply (27W recommended) |
| GPIO | Pin 11 (GPIO17) for Victron input |
| Network | WiFi or Ethernet |
| Component | Specification |
|---|---|
| Computer | Raspberry Pi Zero 2W |
| HAT | Pimoroni Automation Hat Mini |
| Storage | Quality SD card or USB SSD |
| Power | 5V 2.5A supply |
| Relay | Built-in 24V @ 2A max (GPIO16) |
| Display | Built-in 0.96" 160x80 LCD |
Both switches are optional — the system runs fine without either. Together they provide hardware safety at the generator and operator-side mode control. See Hardware Switches for the full operator guide.
| Switch | Schneider P/N | Wires to |
|---|---|---|
| EPO actuator (mushroom E-stop at the generator) | XB4-BS542 | The contact block below — mounts in any 22 mm panel cutout |
| EPO contact block | ZB4-BZ104 (1 NC + 1 NO) | NC in series with the GenSlave start-relay circuit; NO from Auto Hat Mini 5V → IN1 |
| HOA actuator (3-position rotary at the operator) | XB4-BD33 | The contact block below |
| HOA contact block | ZB4-BZ103 (2 NO) | Block A NO → GenMaster GPIO22 (Quiet); Block B NO → GPIO27 (Run); shared common → GND |
| HOA legend plate (optional) | ZBY2-series | Printed QUIET / AUTO / RUN labels around the rotary knob |
Total parts cost: ~$70–120 for both switches plus the optional legend plate. Order from AutomationDirect, Mouser, or Digikey — avoid no-name Amazon listings as Schneider counterfeits are common and defeat the point of paying for rated safety hardware.
This is what I've actually verified in my own off-grid setup — not just what the design supports on paper. Other hardware combinations (different Pi models, alternative relay HATs, other battery monitors) may work fine but have not been field-tested here.
| Area | Tested configuration |
|---|---|
| GenMaster | Raspberry Pi 5, 8GB, Raspberry Pi OS 64-bit |
| GenSlave | Raspberry Pi Zero 2 W |
| Relay HAT | Pimoroni Automation HAT Mini |
| Trigger source | Victron Cerbo GX MK2 relay |
| Network | LAN baseline (same-network operation works with no VPN). Tailscale optional — required for remote administration or when GenMaster and GenSlave are on different networks. Cloudflare Tunnel optional — used to expose the GenMaster web UI publicly. |
| Boot policies tested | fail_safe and preserve_state (both verified across real reboots) |
| Failure tests run | GenMaster reboot mid-operation, GenSlave reboot mid-operation, network loss between master and slave, heartbeat loss triggering GenSlave's independent failsafe, relay forced OFF on failsafe trigger |
A handful of architectural and operational realities to be aware of before deploying this in your own setup:
Fuel usage is estimated, not measured. The fuel-tracking number is
calculated from runtime × your configured consumption-rate values
(fuel_consumption_50 / fuel_consumption_100) — there is no actual
fuel-flow sensor. Accuracy depends on how well your rate values match
real-world performance.
Network and security configuration is non-trivial. This project has
many moving parts (nginx reverse proxy, cloudflared tunnel, Tailscale,
per-route auth, IP allowlists). Read docs/SECURITY.md
end-to-end before exposing GenMaster beyond your LAN.
GenSlave needs privileged GPIO access. It runs as a privileged
container with host networking so the Pimoroni Automation Hat Mini can
drive GPIO. That's a deliberate trade-off — if you compromise the GenSlave
container, you have effective root on the Pi Zero. See
docs/SECURITY.md#container-privilege-model.
Tested with my specific hardware setup; other generators may need wiring/config changes. Generators differ in start-contact polarity, voltage tolerance, dry-vs-wet contacts, hold-time requirements, etc. The relay control logic itself is generator-agnostic, but the physical wiring between GenSlave's relay and your generator's start input is your responsibility to verify.
curl -fsSL https://raw.githubusercontent.com/rjsears/pizero_generator_control/main/genmaster/install.sh | sudo bash
# Clone repository
git clone https://github.com/rjsears/pizero_generator_control.git
cd pizero_generator_control/genmaster
# Run interactive setup
./setup.sh
The setup wizard will:
# Interactive setup
./setup.sh
# Show help
./setup.sh --help
# Use pre-configuration file
./setup.sh --config myconfig.conf
# Validate GenSlave connection (run after GenSlave is set up)
./setup.sh --genslave
# Update GenSlave IP/URL address
./setup.sh --genslaveip
# Show version
./setup.sh --version
To pre-populate generator information during setup, create a gen_info.json file:
# Copy the template
cp genmaster/setup/gen_info.json.template genmaster/setup/gen_info.json
# Edit with your generator details
nano genmaster/setup/gen_info.json
Template contents:
{
"manufacturer": "Generac",
"model_number": "7043",
"serial_number": "ABC123456",
"fuel_type": "lpg",
"load_expected": 50,
"fuel_consumption_50": 1.6,
"fuel_consumption_100": 2.8
}
The setup wizard will detect this file and offer to use these values. Fuel type options: lpg, natural_gas, diesel. Load options: 50 or 100.
# Check container status
docker compose ps
# View logs
docker compose logs -f genmaster
# Access web interface
# https://your-domain.com or https://genmaster (via Tailscale)
GenSlave runs as a Docker container on the Pi Zero 2W:
# SSH into your Pi Zero
ssh [email protected]
# Download and run the setup script
curl -fsSL https://raw.githubusercontent.com/rjsears/pizero_generator_control/main/genslave/setup.sh -o setup.sh
chmod +x setup.sh
sudo ./setup.sh
The GenSlave setup will:
SLAVE_API_SECRET# Check container status
cd /opt/genslave
docker-compose ps
# View logs
docker-compose logs -f
# Test API (requires API key)
curl -H "X-API-Key: your-api-secret" http://localhost:8001/api/health
Configuration is managed through .env file. Key settings:
# Domain Configuration
DOMAIN=genmaster.example.com
# Application
APP_ENV=production
APP_DEBUG=false
APP_SECRET_KEY=<generated-secret>
# Database
DATABASE_USER=genmaster
DATABASE_PASSWORD=<generated-password>
DATABASE_NAME=genmaster
# GenSlave Communication
SLAVE_API_URL=http://genslave.local:8001
SLAVE_API_SECRET=<shared-secret>
# Heartbeat
HEARTBEAT_INTERVAL_SECONDS=10
HEARTBEAT_FAILURE_THRESHOLD=3
# Webhooks (Optional)
WEBHOOK_BASE_URL=https://n8n.example.com/webhook/xxx
WEBHOOK_SECRET=<webhook-secret>
# Generator Information (Optional - can be configured via UI)
GEN_INFO_MANUFACTURER=Generac
GEN_INFO_MODEL_NUMBER=7043
GEN_INFO_SERIAL_NUMBER=ABC123456
GEN_INFO_FUEL_TYPE=lpg # lpg, natural_gas, or diesel
GEN_INFO_LOAD_EXPECTED=50 # 50 or 100
GEN_INFO_FUEL_CONSUMPTION_50=1.6 # gal/hr at 50% load
GEN_INFO_FUEL_CONSUMPTION_100=2.8 # gal/hr at 100% load
# Cloudflare Tunnel
CLOUDFLARE_TUNNEL_TOKEN=<tunnel-token>
# Tailscale (Optional)
TAILSCALE_AUTHKEY=<auth-key>
TAILSCALE_HOSTNAME=genmaster
Enable optional services using profiles:
# Basic stack (GenMaster, PostgreSQL, Nginx)
docker compose up -d
# With Tailscale VPN
docker compose --profile tailscale up -d
# With Cloudflare Tunnel
docker compose --profile cloudflare up -d
# With Portainer
docker compose --profile portainer up -d
# All optional services
docker compose --profile tailscale --profile cloudflare --profile portainer up -d
The dashboard provides at-a-glance status of:
# Get current status
GET /api/status
# Start generator manually
POST /api/generator/start
Content-Type: application/json
{"trigger": "manual"}
# Stop generator
POST /api/generator/stop
# List schedules
GET /api/schedule
# Create schedule
POST /api/schedule
Content-Type: application/json
{
"name": "Morning Run",
"cron_expression": "0 6 * * *",
"duration_minutes": 60,
"enabled": true
}
# Delete schedule
DELETE /api/schedule/{id}
# Get generator info
GET /api/generator-info
# Response
{
"manufacturer": "Generac",
"model_number": "7043",
"serial_number": "ABC123456",
"fuel_type": "lpg",
"load_expected": 50,
"fuel_consumption_50": 1.6,
"fuel_consumption_100": 2.8
}
# Update generator info (partial update)
PATCH /api/generator-info
Content-Type: application/json
{
"manufacturer": "Generac",
"fuel_type": "lpg",
"load_expected": 50
}
# Get exercise schedule
GET /api/exercise
# Response
{
"enabled": true,
"frequency_days": 7,
"start_time": "10:00",
"duration_minutes": 15,
"last_exerci
Content type
Image
Digest
sha256:dee4d64c5…
Size
244.1 MB
Last updated
2 months ago
docker pull rjsears/genmaster:sha-42fe97b