No description
  • C 98.5%
  • Python 0.9%
  • Assembly 0.3%
  • Linker Script 0.2%
  • CMake 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-08 18:39:18 +02:00
.vscode Doku 2026-08-08 13:38:53 +02:00
case DXF der EVA-Schaumteile hinzugefügt 2026-08-08 18:39:18 +02:00
firmware Strommessungen dazugefügt 2026-08-08 15:41:54 +02:00
hardware Doku 2026-08-08 13:38:53 +02:00
ledcalc Doku 2026-08-08 13:38:53 +02:00
ppk_images Strommessungen dazugefügt 2026-08-08 15:41:54 +02:00
.gitignore initial 2026-06-18 13:30:39 +02:00
README.de.md Strommessungen dazugefügt 2026-08-08 15:41:54 +02:00

Solardekolampe (Weil Kaufhaus-Solarlampen meist Schrott sind)

Rendering der Platine

Eines schönen Tages im Frühsommer 2026 habe ich aus ein bisschen verzinktem Draht ein paar Halterungen für alte Marmeladengläser gebastelt. Mein Schatz hat dann Teelichter reingestellt. Romantisch, klar aber ein Fall für eine ordentliche Ingenieurslösung.

Die Idee war geboren: Das Ganze wird vollautomatisiert als autarke Solarleuchte umgesetzt. Gesagt, getan. Die grösste mechanische Hürde? Ein passendes Gewinde für Twist-Off TO82-Gläser zu konstruieren mangels ordentlicher Dokumentation eine kleine Odyssee.

Auf der Elektronikseite stand natürlich die eigentliche Disziplin an: Das System auf kompromisslosen Low-Power-Betrieb zu trimmen.


Features

  • LFP-Akku: Robuste Chemie, lange Lebensdauer.
  • Solarzelle: Energiequelle und Dämmerungssensor in einem.
  • STM32L011 Microcontroller: Effizient programmiert via LL-API.
  • Volles Akkuschutz-Paket: Tiefentlade-, Überlade- und Frostschutz.
  • Intelligente Dämmerungssteuerung: Automatische Erkennung über das Solarpanel.
  • Konstante Helligkeit: Batteriespannungskompensation verhindert Verblassen bei sinkender Akkuspannung.
  • Dämmerungskompensation: Höhere Leuchtkraft in der blauen Stunde, dezent gedimmt um Mitternacht.

Hardware-Architektur

Herzstück des Designs ist der STM32L011. Warum genau dieser Baustein?

  • Extrem niedriger Ruhestrom, vor allem im STOP-Mode.
  • Integriertes EEPROM erspart externe Speicherchips für Parameter.
  • Vertraute Architektur ohne böse Überraschungen.

Ladepfad & Schutzschaltungen

Zwischen Solarzelle und Akku sitzt ein MAX40200 als ideale Diode. Der Vorteil gegenüber einer klassischen Schottky-Diode: nahezu null Spannungsabfall. Über den Enable-Eingang lässt sich der Ladevorgang zudem sauber steuern.

Gegen versehentliches Falschpolen ist ein AO4301A P-Kanal-MOSFET integriert. Zugegeben: Bei der aktuellen Revision mit punktgeschweissten Nickelstreifen und codiertem Stecker ist er fast schon Paranoia schliesslich wandert da nicht mal eben eine Alkali- oder NiCd-Zelle rein. In Revision 2 fliegt der FET wohl raus.

Messschaltungen & Bauteil-Anekdoten

Für das Schalten des Solar-Spannungsteilers kommt ein BSD235C Complementary-MOSFET zum Einsatz. Welches Reitpferd mich bei der Bauteilauswahl geritten hat, weiss ich auch nicht mehr (wahrnehmbar im Angebot...). Zum Handlöten auf Dauer jedenfalls kein Vergnügen. Revision 2 bekommt eine saubere Kombi aus AO4300A und AO4301A.

Apropos Spannungsteiler: Ausgelegt auf 2 \times 47\text{ k}\Omega. Die Summe ist bewusst etwas kleiner gewählt als der Pull-Down am MAX40200. Damit lässt sich vorab prüfen, ob Laden überhaupt Sinn ergibt: Verbraten wir durch das Durchschalten des MAX40200 mehr Strom am Pull-Down als die Solarzelle liefert, lassen wir es einfach bleiben.

An der Solarzelle sichert eine BZT52B5V6 Z-Diode die Schaltung ab. Nicht, weil die aktuelle Zelle im Leerlauf über 5,5 V kommen würde aber man weiss ja nie, was spätere Bauteil-Bestellungen so anliefern. Der MAX40200 quittiert alles über 6 V mit Rauchzeichen, also bleibt er geschützt.

Die LED-Ansteuerung übernimmt ein stinknormaler AO3400A (Low-Side) via PWM. Der verbaute 33-$\Omega$-Vorwiderstand passt perfekt für die gängigen 60 mm, 80 mm und 110 mm LED-Filamente aus Fernost.

Geflasht wird über eine kompakte STDC14-Schnittstelle, Verbindungen laufen über kleine JST-PH-Stecker.

Warum kein dedizierter LFP-Schutz-IC? Ganz einfach: Das Schutz-IC würde mehr Eigenstrom ziehen als die gesamte restliche Schaltung zusammen. Die Batterieüberwachung übernimmt der STM32L011. Sollte die Akkuspannung so weit einbrechen, dass der STM32 in den Brown-Out rasselt, ist die Zelle ohnehin durch. Da rettet auch kein Schutz-IC mehr was.


Akku- und Solarzellenwahl

Die Wahl fiel auf einen LFP-Akku (Lithium-Eisenphosphat) mit 3,2 V Nennspannung. Die Chemie ist robust, langlebig und unempfindlich gegen Tiefentladung. Und natürlich, weil sie keinen Spannungswandler braucht 😜. Der STM32 läuft im bereich der LFP optimal. Ach ja, ist eine AAA Zelle ohne Schutzbeschaltung. Bei der Solarzelle habe ich genommen, was Fernost hergegeben hat. Angeblich 4V, 100mA. Naja, wie auch immer, der LFP wird damit voll.

⚠️ WARNUNG (Rev. 1 Bug)

In Revision 1 existiert eine schaltungstechnische Falle: Wird der Batteriestecker gezogen, während die Solarzelle lädt, fehlt der Zelle die Last. Die Spannung schnellt auf Leerlaufniveau hoch (bis zu 5,5 V oder mehr). Das kann den STM32L011 auf direktem Weg ins Silizium-Jenseits befördern.

Revision 2 To-Do

Für die nächste Platinen-Iteration stehen folgende Anpassungen an:

  • Verpolschutz-FET ersatzlos streichen.
  • High-Side-Schalter des Solar-Spannungsteilers auf diskrete Standard-MOSFETs umstellen.
  • Pull-Ups und Pull-Downs ausmisten (bis auf den Gate-Schutz am MAX40200 gegen unkontrolliertes Laden bei Prozessor-Hängern).
  • Konsequentes Poka-Yoke beim Stecker-Layout:
    • Solar: 2-polig.
    • LED: 3-polig (V_{\text{SYS}} mit Widerstand, V_{\text{SYS}} direkt für Filament-Ketten mit integriertem Widerstand, GND-PWM).
    • Akku: 4-polig. Das Plus der Solarzelle wird über die Batteriensteckbrücke geschleift. Wird der Akku getrennt, wird das Solarpanel physikalisch mitgetrennt. Überspannung unmöglich.

Software-Konzept

Keine Magie, sondern solides C unter Nutzung der ST Low-Layer (LL) APIs.

EEPROM-Mapping

Die Konfigurationsdaten liegen übersichtlich im internen EEPROM:

Parameter Einheit Funktion / Beschreibung
magic_number - Revisions-Check. Stimmt der Wert beim Booten nicht, werden Default-Werte ins EEPROM geschrieben.
chg_stop_voltage_mv mV Ladeschlussspannung. Bei Erreichen wird der Solar-Pfad aufgetrennt.
chg_start_voltage_mv mV Hysterese-Schwelle für das Wiedereinschalten des Ladevorgangs.
freeze_voltage_mv mV Abschaltwert für den Frostschutz via Die-Temperatur. (Details zur Kalibrierung siehe unten).
chg_solar_delta_mv mV Erforderlicher Spannungsüberschuss der Solarzelle gegenüber dem Akku zur Ladefreigabe.
led_on_voltage_mv mV Mindestspannung für die Freigabe der LED bei Dämmerung.
led_off_voltage_mv mV Abschaltschwelle der LED zum Zellenschutz.
protection_voltage mV Kritische Schwelle. Darunter wechselt das System in ein langes Schlafintervall.
dusk_voltage_mv mV Solar-Schwellspannung für "Abenddämmerung" (LED EIN).
dawn_voltage_mv mV Solar-Schwellspannung für "Morgengrauen" (LED AUS).
night_voltage_mv mV Referenzwert für tiefste Nacht (Dämmerungsdimmung).
max_battery_mv mV Referenz-Spannung für die Kurven-Linearisierung aus dem Python-Tool.
comp_base - Offset der Kompensationskurve.
comp_slope - Steigung der Kompensationskurve.
dim_night - Ziel-Dimmwert für die Nacht (0xFFFF = 100% / ungedimmt).
default_wake_interval s Normales Mess- und State-Machine-Intervall im Aktivbetrieb. Achtung: Zu gross gewählte Werte bergen Überladegefahr!
protection_wake_interval s Sparsames Aufwachintervall bei tiefem Akkustand.
adc_calibration_interval s Re-Kalibrierungsintervall für den internen ADC.
led_on_grace_period_seconds s Mindestlaufzeit nach dem Einschalten. Verhindert störendes Flackern in der Übergangsphase.

Ablauf & State Machine

  1. Boot: Einlesen der EEPROM-Parameter. Ist die magic_number ungültig, zieht das System Werkseinstellungen nach.
  2. Status-Blitz: Ein kurzes dreifaches Blitzen signalisiert den erfolgreichen Systemstart nach Flash- oder Reset-Vorgängen.
  3. Kalibriermodus: Falls im EEPROM gefordert, wird die Sensor-Routine gestartet.
  4. State Machine Loop:
    • Messen: Ladezweig kurz trennen, Spannungsteiler aktivieren, Solar-, Akku- und Temperaturwerte erfassen. Spannungsteiler sofort deaktivieren, um Treiber-Verluste zu minimieren.
    • Akkuschutz: Liegt die Spannung unter protection_voltage, bleibt die LED aus und das System legt sich für das verlängerte protection_wake_interval schlafen.
    • Regelbetrieb: Verarbeiten der Dämmerungs- und Ladelogik gemäss EEPROM-Parametern.

Kalibrierung

1. LED-Helligkeitskompensation

Die Vermessung der Diodenkennlinie und die Berechnung der Korrekturfaktoren läuft ausgelagert über ein Python-Skript. Die errechneten Werte (comp_base, comp_slope) wandern direkt ins EEPROM. (Siehe ledcalc/README.de.md).

2. Frostschutz-Kalibrierung (Kühlschrank-Methode)

Da ST dem STM32L011 ab Werk nur einen einzigen Kalibrierwert bei 130 °C spendiert hat und die Steigung von Batch zu Batch streut, wird der Gefrierpunkt pragmatisch ermittelt:

  1. Der Wert 0xCABE (calibrate) wird an die Adresse freeze_voltage_mv im EEPROM geschrieben.
  2. Die Firmware erkennt das Flag beim nächsten Booten und schaltet in den Kalibriermodus (quittiert durch ein periodisches Kontroll-Blitzen).
  3. Ab in den Kühlschrank: Das Board wandert für ca. 2 Stunden ins Gemüsefach, bis das Silizium auf stabile ~4 °C durchgekühlt ist. (Fragen der Partnerin bezüglich blitzender Platinen im Gemüsefachtechnisch versiert ignorieren).
  4. Die Software misst fortlaufend die VCC- sowie Die-Temperaturspannung und speichert das ermittelte Spannungsminimum.
  5. Nach dem Entnehmen erwärmt sich die Schaltung wieder. Die Firmware erkennt den Anstieg, schreibt die minimale Messspannung als freeze_voltage_mv ins EEPROM und beendet den Modus. Das Geblitze stoppt Kalibrierung abgeschlossen.

Sicherheitsnetz: Sollte der Akku während des Kühlschrank-Trips leergelaufen sein, schreibt das System einen Notfall-Wert ins EEPROM, der das Einschalten der LED dauerhaft blockiert. Der Akku lädt sich im nächsten Sonnenlicht voll auf, woraufhin der Prozess mit voller Batterie wiederholt werden kann, nachdem die Chefin gemerkt hat, dass seit mehreren Nächten die Lampe nicht mehr leuchtet.

3. Dämmerungsschwellen

Praxisnahes Vorgehen: In der gewünschten Abenddämmerung den Debugger anhängen, die anliegende Solarspannung auslesen und diesen Wert als dusk_voltage_mv hinterlegen. Für dawn_voltage_mv ein paar Millivolt Hysterese draufschlagen. Fertig.

Config-Tool

Unter firmware/ConfigTool liegen ein paar Tools, die das EEPROM Auslesen und schreiben können. Ein pyocd-kompatibles Debug-Interface vorausgesetzt, lassen sich die EEPROM-Parameter bequem vom PC aus anpassen. Ich konnte mich noch nicht definitiv für eins eintscheiden, nutze aber meist das aio.py (aio = All In One). ⚠️ Aufgepasst: Das Tool ist noch in der Entwicklung und ein kleiner Satan. Mit --set wird manchmal das ganze EEPROM zurückgesetzt. Ich empfehele EEPROM auslesen, anpassen, einspielen. Die Scripts haben ein --help.

Messungen

Ok, mal schauen, ob wir das Ziel von unter 2.5uA erreicht haben. Die Messungen haben mit dem Nordic PPK2 stattgefunden, und zwar im Source Meter Modus, mit 3.6V Ausgangsspannung.

Im Idle

Wir müssen zwischen CHARGING/IDLE und LED_ON unterscheiden. Wenn die LED eingeschaltet ist können wir nicht so tief schlafen wie wenn sie ausgeschaltet ist, ansonsten geht uns die PWM aus, da ihr der Takt fehlt. Idle Cycle Hier also ein Idle Cycle, der 10 Sekunden dauert. Also im Prinzip. Als erstes sehen wir, dass der STM32L011 es nicht so genau nimmt mit dem internen RC-Oszillator. Er macht aus 10 Sekunden 8.337 Sekunden. Naja, halb so wild, wir steuern keine Cruise Missile. Sehr schön zu sehen ist aber, dass wir durchschnittlich während des Zyklus 2.44uA ziehen. Das ist ein Traumwert, würd ich sagen. In einer idealen Welt würde unser STM32 damit an einer LFP-AA-Zelle mit aufgedruckten 600mAh PMPO also gute 250.000 Stunden, also 28 Jahre, durchhalten. OK, chemische Selbstentladung etc... und die soll sich ja nicht selbst befriedigen, sondern die LED mit Strom versorgen.

Hier noch ein Bild von der Wach-Phase, wo er die Messungen macht und die State Machine durchläuft. Idle Awake Die Wachphase dauert also etwas länger als 9ms, und wir ziehen in dieser Zeit im Durchschnitt 135uA. Sehr schön. Woher der langsame anstieg am Anfang kommt, und dieser verdächtige Abfall... ich weisse es nicht.

Mit LED_ON

aber ohne LED dran, damit wir die Unterschiede sehen im Schlafbedarf (und natürlich was der FET beim umladen braucht): LED_ON Cycle Erwartungsgemäss ist der Stromverbrauch höher. Also viel höher, knapp über 200uA. Naja, im Vergleich zur LED, die ja auch noch dazu kommt, ist der Verbrauch aber noch vertretbar. Im Wachzyklus sieht man jetzt nich mehr so viel unterschied. Das ist eigentlich nur noch das Analog-Gerumple, was noch dazu kommt. LED_ON Awake

Fazit

Wenn wir über rund 20s Zykluszeit bleiben, haben wir einen Standby-Stromverbrauch von unter 2.5uA. Das ist ein Traumwert. Alleine ein LFP Schutz-IC braucht normalerweise so viel. Das darf so bleiben. Wie gesagt, wenn wir laden, kommen nochmals die 100kOhm am Pull-Down der idealen Diode dazu, also so um die 3-3.6uA (was mehr ist als dass der Controller braucht). Die tuen aber auch nicht wirklich weh, da wir ja beim Laden Solarstrom versauen dürfen. Also alles gut.

Gehäuse

Im Ordner case liegen die Produktions-Dateien für das Gehäuse. Die Konstruktion ist auf Twist-Off TO82 Gläser ausgelegt. Wer andere Gläser nutzen möchte, muss die Gewindeaufnahme anpassen. Und so. Die DXFs sind für den Laser, damit werden aus 2mm EVA-Schaum (selbstklebend) die Dichtung für Innendeckel (wird auf den Innendeckel gegklebt) und die PCB-Unterlage gelasert. Zudem braucht Ihr noch 4 2.6x8mm PT-Schrauben. Ich mag jetzt nicht erklären, wie alles zusammengehört, schaut sonst bei OnShape nach.