followmysun
Single axis adjustment for my solar panel
Astronomisch geführte Sonnenstand-Nachführung für ein einzelnes PV-Modul auf einem Gartenhaus-Dach. Basierend auf ESP8266 (ESP12F-Relay-X4), MPU-6050-Beschleunigungssensor und einem 12 V Linear-Aktuator – mit MQTT-Anbindung an Home Assistant inkl. Sturm-Notfallmodus.

Was macht das Ding?
Ein einzelnes PV-Modul auf dem 32°-Dach eines Gartenhauses wird durch einen 12 V Linear-Aktuator zwischen 32° (flach auf dem Dach) und ca. 59° (Aktuator voll ausgefahren) verstellt. Ein MicroPython-Programm auf einem ESP12F berechnet aus Datum, Uhrzeit und Standort den optimalen Panel-Winkel zur Sonne, misst per MPU-6050-Beschleunigungssensor den aktuellen Ist-Winkel und steuert über zwei Relais die Polarität am Aktuator.
Alle Werte werden per MQTT an einen Broker geschickt – damit baut sich in Home Assistant ein Live-Dashboard. Über ein Command-Topic (cmnd/solar/EMERGENCY = on) lässt sich das Panel bei Unwetterwarnung sofort flach auf das Dach fahren.
Features
- Astronomische Sonnenstand-Berechnung für jeden Tag des Jahres und jede Tageszeit
- MPU-6050-Regelung mit asymmetrischer Hysterese (Start ab 2° Abweichung, Stopp ab 0,5° + Overshoot-Erkennung)
- MQTT mit JSON-Vollpayload + einzelnen Topics für Home-Assistant-Sensoren
- Notfallmodus über MQTT (
cmnd/solar/EMERGENCY) – Panel sofort in Schutzposition - Hardware-Watchdog (~3 s) gegen ESP-Hänger
- WebREPL zur Wartung übers WLAN (kein USB nötig nach Einbau)
- Mini-Webserver zur manuellen Steuerung im Browser
- Kalibrierungs-Layer mit Offset/Sign – Sensor-Achsenrichtung muss nicht physisch perfekt sitzen
Hardware
| Bauteil | Funktion |
|---|---|
| ESP12F-Relay-X4 v1.2 | ESP8266 + 4 Relais, davon 2 genutzt |
| MPU-6050 (GY-521) | Beschleunigungssensor zur Winkelmessung |
| Linear-Aktuator 12 V mit Endschaltern | Verstellt das Panel-Gestell |
| DC/DC-Stepdown-Wandler | 12 V → 5 V Versorgung des ESP |
| CAT5-Kabel ~2,5 m | Verlängert I2C-Bus vom Controller zum Sensor am Panel |
Steuerung im Gartenhaus

Der Controller hängt geschützt unter der Dachschräge. Die zwei roten LEDs zeigen, welches Relais gerade aktiv ist.
Stromversorgung

Ein einstellbarer Stepdown-Wandler erzeugt die 5 V für den ESP aus der 12 V Aktuator-Versorgung – ein Netzteil reicht für alles.
MPU-Sensor am Panel

Der MPU-6050 sitzt in einem 3D-gedruckten Gehäuse direkt am PV-Rahmen und misst die Neigung gegen die Schwerkraft. Verkabelt mit CAT5 zum Controller.
Schnellstart
- Hardware nach Pinbelegung verdrahten (siehe docs/hardware.md)
- MicroPython auf ESP12F flashen (docs/installation.md)
src/env.example.py→env.pykopieren und Werte einfüllen (WLAN, MQTT, Sensor-Achsen)- Code als
.mpykompilieren (sonst MemoryError beim Parsen):python -m mpy_cross solar_main.py - Dateien hochladen (USB oder WebREPL):
boot.py,main.py,env.py,mpu6050.mpy,solar_main.mpy - Strom an – fertig. Webseite unter
http://<esp-ip>/, MQTT-Topics untertele/solar/...
Detaillierte Anleitung: docs/installation.md
Migration auf ESP32 mit externer Antenne: Wenn ihr das Setup an einem WLAN-schwachen Standort betreibt (z. B. Gartenhaus / Schuppen), gibt es eine ausführliche Umbau-Doku vom ESP12F-Relay-X4 auf den Olimex ESP32-EVB-EA Rev.L mit IPEX-Externantenne: docs/hardware-migration-esp32-evb.md — inkl. Mermaid-Diagrammen für IST- und SOLL-Verkabelung sowie dem korrekten 12 V-Split über einen Mini-360-Buck (das neue Board ist strikt 5 V!).
3D-Druck-Gehäuse für den Olimex-Controller

Für die Überkopfmontage des neuen Controllers im Schuppen unter dem Dach gibt es jetzt ein eigenes parametrisches Gehäuse in v0.3. Es bietet Zugang zu den sechs Aktuator-Schraubklemmen, Micro-USB, PWR1, UEXT/Dupont, externer WLAN-Antenne und RST1; der nicht benötigte CAN-Anschluss bleibt geschlossen.
- Gehäuse-Dokumentation – Maße, Anschlussöffnungen, Explosionsansicht, Druckhinweise und Fit-Test
- OpenSCAD-Quelle + Build-Script – erzeugt Base, Lid, Reset-Button, Fit-Test und ZIP reproduzierbar
MQTT-Topics für Home Assistant
| Topic | Richtung | Beispiel | Beschreibung |
|---|---|---|---|
tele/solar/SENSOR | ESP→ | JSON | Vollpayload mit allen Werten |
tele/solar/SENSOR/PanelAngle | ESP→ | 35.4 | Aktueller Panel-Winkel (kalibriert) |
tele/solar/SENSOR/TargetAngle | ESP→ | 42.1 | Soll-Winkel |
tele/solar/SENSOR/SunAngle | ESP→ | 45.0 | Berechneter optimaler Winkel |
tele/solar/SENSOR/MotionText | ESP→ | Hoch | Stopp / Hoch / Runter |
tele/solar/SENSOR/MinAngle | ESP→ | 32.0 | Mechanisches Untermass |
tele/solar/SENSOR/MaxAngle | ESP→ | 58.7 | Mechanisches Obermass |
tele/solar/SENSOR/Emergency | ESP→ | false | Notfall-Status |
cmnd/solar/EMERGENCY | →ESP | on / off | Notfall an/aus (retained!) |
Vollständig: docs/mqtt.md · NodeRED-Sturm-Automatik: docs/nodered-stormwatch.md
Notfall-Modus für Sturmwarnung
Aus Node-RED bei Unwetterwarnung:
msg.topic = 'cmnd/solar/EMERGENCY';
msg.payload = 'on';
msg.retain = true; // wichtig: ueberlebt ESP-Reboot
return msg;
Der ESP fährt das Panel sofort auf MIN_ANGLE (Dachneigung, flach) und ignoriert Auto-Modus + manuelle Web-Befehle. Bei off wechselt er zurück in den Astro-Modus.
Repo-Struktur
followmysun/
├── README.md
├── src/ # Tracker-Firmware
├── docs/ # Detail-Dokumentation
│ ├── hardware.md
│ ├── installation.md
│ ├── mqtt.md
│ ├── calibration.md
│ ├── tracker-debugging.md
│ ├── hardware-migration-esp32-evb.md
│ ├── housing-esp32-evb.md # 3D-Druck-Gehäuse Olimex v0.3
│ └── img/
├── hardware/
│ └── enclosure/
│ └── olimex-esp32-evb-ea-v0.3/ # SCAD, Build, Rev-L-Koordinaten, QA
└── _config.yml
Tech-Stack
- MicroPython 1.28 auf ESP8266 (ESP12F)
- mpy-cross für Bytecode-Kompilierung (sonst RAM-Limit)
- paho-mqtt / mosquitto als Broker
- MPU-6050 mit
atan2-basierter Roll-Berechnung - SoftI2C @ 100 kHz (wegen 2,5 m CAT5-Strecke)
Details siehe docs/software.md.
Lessons Learned
Drei Sachen, die mich am meisten Zeit gekostet haben:
mqtt.ping()darf nicht in jeder Loop-Iteration aufgerufen werden – sonst stempelt der Broker dich als misbehaving und schmeißt dich mitECONNRESETraus. Throttling auf 1×/30 s.- ESP8266 WLAN braucht aktiven Outbound-Traffic, sonst lernen Router/Switches die MAC nicht zuverlässig zurück – ein UDP-Keepalive pro Sekunde an die Gateway-IP löst’s.
solar_main.pyist mit 33 KB zu groß zum Parsen auf dem ESP8266 (Heap ~35 KB). Lösung: vorher mitmpy-crosszu.mpy(10 KB) kompilieren.
Lizenz
MIT – siehe LICENSE.