Solar LED-Strahler mit ATtiny
von Stephan Laage-Witt
Elektronik-Labor Projekte AVR
Neulich
ist es wieder passiert: Das Warten an der Kasse unseres lokalen
Baumarkts führt an den Regalen mit Billig-Artikeln vorbei. Die
verkaufsoptimierte Anordnung funktionierte bestens: Wider besseren
Wissens landete ein Solar-Strahler für den Garten im Einkaufswagen und
wechselte an der Kasse für wenige Euro den Besitzer. Und das nicht zum
ersten Mal! Zuhause kam, was kommen musste: Nach einem sonnigen Tag
schaltete sich am Abend ein unangenehm kalt-weißes Licht ein, das im
Laufe des Abends schnell an Helligkeit verlor und den Rest der Nacht
funzelig vor sich hin leuchtete. Nach dem Aufschrauben fand ich eine
sehr minimalistische Ausstattung, bestehend aus drei parallel
geschaltete 5 mm-LEDs, die über ein vierbeiniges IC mit dem kleinen
Solarpanel und einer NiMH-Zelle verbunden waren. Eine Festinduktivität
deutete auf eine „joule-thief“-Schaltung hin, die bei abnehmender
Spannung des Solarpanels die LED einschaltet und am Morgen bei
zunehmender Spannung wieder ausschaltet. Es ist klar, dass für dieses
Design der Preisdruck die Oberhand hatte. Insofern macht das Gerät, was
es soll, aber mit unbefriedigendem Ergebnis.
Die Frage ist: Wenn ich
bereit bin, ein paar mehr Euro zu investieren, wie kann man es besser
machen? Auf der Wunschliste stehen: Helleres und warm-weißes Licht mit
gleichbleibender Leuchtstärke, begrenzte Brenndauer am Abend zum
schonenden Umgang mit der beschränkten Solarenergie und ein pfleglicher
Umgang mit dem Akku für eine lange Lebensdauer.
Hier möchte ich
einen Vorschlag vorstellen, der diese Kriterien erfüllt – und
inzwischen unseren heimischen Garten an verschiedenen Punkten
erleuchtet.
SMD-LED
Mehr
Licht braucht in erster Linie eine andere LED. Die ursprünglichen
5mm-LEDs wurden durch eine SMD-LED ersetzt. Ich habe mich für die
STW8Q14C von Seoul Semiconductor entschieden, die für 100mA
Dauerleistung bei etwa 3V ausgelegt und für wenig Geld zu habe ist.
Jetzt war es auch an der Zeit, eine angemessene Farbe für die
Gartenbeleuchtung zu wählen. Ich bevorzuge warm-weißes Licht, das den
Ursprüngen jeder Gartenbeleuchtung - Fackeln oder Kerzen - einigermaßen
nahekommt. Die LED trotz SMD-Bauform ausreichend groß, dass man sie
ohne Probleme auf einer Lochrasterplatine auflöten kann. Zusammen mit
einem Vorwiderstand von 10 Ohm auf der Rückseite der Platine kommt die
neue LED auf den Reflektor und wird dort mit eine Tropfen
Sekundenkleber befestigt. Das Scheinwerfer-Gehäuse vom Baumarkt erfüllt
hier gute Dienste. Es zeigte sich, dass diese Anordnung ein sehr
schönes und kräftiges Licht produziert.
SMD- LED an ihrem Arbeitsplatz
Energieversorgung
Die
stärkere LED braucht mehr Strom, was unweigerlich zu einem größeren
Solarpanel und einem stärkeren Energiespeicher führt. Von früheren
Projekten hatte ich noch ein Panel mit 15 x 8 cm Größe in der
Bastelkiste. Das Panel liefert maximal 5.5V und einen Kurzschlussstrom
von 150mA. Als Energiespeicher kommen drei NiMH-Zellen, Größe AA, aus
dem besagten Baumarkt zum Einsatz. Die Akkus werden in Reihe zu einem
„Battery-Pack“ zusammengeschaltet und liefern so eine Ausgangsspannung
zwischen 3.0 und etwa 3.8V. Das Solarpanel versorgt den Akku über eine
Schottky-Diode (BAT46), die die Batterie vor Entladung schützt. Meine
Messungen zeigen einen Ladestrom bei maximalem Sonnenschein zwischen
100mA am leeren und 50mA am vollen Akku. An sehr sonnigen Tagen muss
der Akku gegebenenfalls 50mA Dauerladung aushalten, was mir akzeptabel
erscheint. Eine Abschaltung bei Vollladung ist also nicht vorgesehen.
Das Kraftwerk: Solarpanel und NiMh-Akku
Schaltzentrale
Jetzt
wird noch eine Steuerung benötigt, um die Komponenten miteinander
arbeiten zu lassen. Hier kommt der bewährte ATtiny45 zur Anwendung. Der
Mikrocontroller hat 3 Aufgaben.
Erstens schaltet er die LED am
Abend abhängig von der Spannung des Solarpanels ein und nach einigen
Stunden wieder aus. Schließlich besteht kein Bedarf, die
Gartenbeleuchtung die ganze Nacht zu betreiben. Stattdessen sollte eine
Akkufüllung auch bei schlechtem Wetter für mehrere Abende reichen.
Zweitens
sorgt der ATtiny als Step-Up Wandler mit Hilfe einer Speicherdrossel
für eine konstante Spannung an der LED, so dass die Helligkeit immer
gleichbleibend und unabhängig vom Ladezustand des Akkus ist.
Und
drittens beobachtet der Prozessor die Akkuspannung und schaltet den
LED-Verbraucher aus, wenn die Akkuspannung unter den kritischen Wert
von 3.0V fällt. So wird eine schädigende Tiefentladung des Akkus
vermieden.
Schaltplan des Solar-LED Strahlers
Im
Zentrum der Schaltung steht der Mikrocontroller. Er bekommt Input von 2
Quellen, die mit Hilfe des ADC ausgelesen werden: ADC-Kanal 2 misst die
Spannung am Solarpanel vor der Schottky-Diode, und ADC-Kanal 3 die
Akkuspannung. Als Referenz für den ADC wird die die interne
Referenz-Spannung von 1.1V verwendet. Deshalb werden die externen
Spannungen mit einem Spannungsteiler von 100 versus 33 kOhm auf das
passende Niveau reduziert.
Der Ausgang PB1 schaltet die LED mit
Hilfe eines P-Kanal FETs (IRLML2244) ein oder aus. Seit einiger Zeit
gibt es MOSFETs, die auch mit 3V Steuerspannung durchschalten. Der
MOSFET hat gegenüber einem bipolaren Transistor den Vorteil eines sehr
kleinen Durchlasswiderstandes in eingeschaltetem Zustand. Dadurch
verbessert sich der Wirkungsgrad der Schaltung. Außerdem kann das Gate
direkt ohne Vorwiderstand an den Ausgang des Mikroprozessors
angeschlossen werden. Nebenbei bemerkt sind die hier verwendeten Power
MOSFETs ausgesprochen preisgünstig und in der Tat die billigsten
Transistoren, die ich bisher je erworben habe.
Eine Besonderheit
dieser Schaltung ist die Spannungserhöhung mit Hilfe eines Step-Up
Wandlers. Der Ausgang PB0 produziert ein PWM-Signal von etwa 32kHz, das
mit Hilfe eines N-Kanal FETs (IRLML6244) die Speicherdrossel periodisch
auf Masse legt. Die Schottky-Diode koppelt die erhöhte Spannung aus,
die dann mit dem Elko geglättet wird. Das Ausmaß der Spannungserhöhung
kann über das Duty-Verhältnis des PWM-Signals eingestellt werden, was
per Software geschieht. Dazu wird der Spannungswert am Ausgang über
einen Spannungsteiler auf den ADC-Kanal 1 gegeben. Je nachdem, ob der
Wert unter oder über der Zielgröße liegt, wird das Duty-Verhältnis des
PWM-Signals angepasst. Diese Regelung klappt sehr gut und funktioniert
mit minimaler Hardware. Der Nachteil, dass die Regelung recht langsam
von statten geht, ist für diese Anwendung kein Problem. Es geht
schließlich nur darum, die Ausgangspannung (und damit den LED-Strom)
bei abfallender Akkuleistung konstant zu halten. Und das sind
Veränderungen im Minuten- oder Stunden-Bereich.
Software
Die
Software ist recht überschaubar und passt in eine kurze Datei
(solar_led.c). Das Programm ist um 3 verschiedene Systemzustände herum
organisiert:
• STATE_DAY ist der Betrieb am Tag. Der Akku wird geladen und die LED ist ausgeschaltet.
•
STATE_NIGHT nach Einbruch der Dämmerung. Wenn die Spannung an der
Solarzelle unter einen Grenzwert absinkt, wird die LED eingeschaltet.
•
STATE_BAT_LOW nach Absinken der Akku-Spannung unter 3.0V, und
STATE_TIME_OUT nach Ablauf der geplanten Leuchtdauer sind eigentlich
zwei verschiedene Zustände, die aber identische Eigenschaften haben. In
beiden Fällen ist die LED ausgeschaltet. Das System wartet auf den
nächsten Morgen mit einer ansteigenden Spannung am Solarpanel.
System-Zustände und ihre Übergänge
Zur
Verwaltung der Zustände wird ein regelmäßiger Timer-Interrupt
verwendet, der die CPU einmal pro Sekunde aus dem Sleep-Modus weckt.
Hier kommt der Watchdog-Timer zum Einsatz, der im Gegensatz zu den
anderen Timern den Vorteil eines sehr geringen Strombedarfs. Außerdem
erlaubt er die Verwendung des „PWR_DOWN“ Sleep-Mode, was den Chip auf
minimal Stromverbrauch trimmt. Tatsächlich verbraucht die Schaltung bei
abgeschalteter LED nur 30 µA. Der Watchdog-Timer hat den Nachteil, dass
seine Frequenz etwas ungenauer ist im Vergleich zu Timer0 und Timer1.
Bei dieser Anwendung kommt es aber nicht auf exaktes Timing an.
Für
den Step-Up Wandler produziert der ATtiny ein PWM-Signal, dessen
Duty-Verhältnis über OCR0A eingestellt wird. Der Wert 255 entspricht
dem kleinsten Duty-Cycle. Kleinere OCR0A-Werte erhöhen den Duty-Cycle
und damit die Ausgangsspannung. Die Routine zur Regelung wird einmal
pro Sekunde aufgerufen und ist sehr minimalistisch. Mit jedem Durchlauf
wird OCR0A um den Wert 1 erhöht oder vermindert. Wie schon gesagt
ergibt das eine sehr langsame Regelung. Natürlich könnte man einen
effizienteren Regelalgorithmus implementieren, aber für diese Anwendung
ist die Langsamkeit eine sehr sympathische Eigenschaft. Aus einer
nostalgischen Perspektive ist es schön zu sehen, dass die Lampe nach
dem Einschalten 30 oder 40 Sekunden benötigt, um volle Helligkeit zu
erreichen, ganz wie die alten Natriumdampflampen in früheren
Straßenlaternen …
Die Konstante TIME_OUT definiert die Brenndauer
der LED in Sekunden. Im Programm-Listing ist der Wert von 14400
vorgegeben, was 4 Stunden entspricht. Hier sind natürlich beliebige
andere Werte möglich.
Das Programm verwendet eine
Umschaltverzögerung von 5 Minuten. Ohne diese Verzögerung besteht die
Gefahr, dass in der Nacht eine kurzeitige Beleuchtung der Solarzelle
(z.B. ein vorbeifahrendes Auto) den Prozess neu startet. Die Dauer der
Verzögerung kann über die Konstante SWITCH_DELAY eingestellt werden.
Der
ATtiny arbeitet in dieser Anwendung mit dem internen 8MHz
Taktgenerator. Dazu muss die CKDIV8-Fuse gelöscht werden. Alle anderen
Fuse-Einstellungen bleiben unverändert.
Praktisches
Mit
etwas Geduld und einer ruhigen Hand kann man die Schaltung auf einem
recht kleinen Stück doppelseitiger Lochraster-Platine zusammenbauen.
Das folgende Bild zeigt ein mögliches Layout.
Layout
auf einer doppelseitigen Lochrasterplatine. Schwarze Linien zeigen
Verbindungen auf der Unterseite, und roten Linien Drahtbrücken auf der
Oberseite. Die Speicherdrossel L wird senkrecht gestellt. Die
Anschlüsse für Akku, Solar-Panel und LED werden mit flexiblen Kabeln
herausgeführt.
Die FETs sind kleine SMD-Bauteile, die aber
mit dem Lötkolben noch einigermaßen handhabbar sind. Es hat sich
bewährt, sie ziemlich am Anfang der Lötarbeiten einzubauen. Dafür lasse
ich kurze versilberte Kupferdrähte auf der Oberseite der Platine
passend herausstehen, auf die die FETs dann aufgelötet werden. Dasselbe
gilt für die SMD-Speicherdrossel, die aber erfreulicherweise wesentlich
grösser ist.
Montage
der SMD FETs. Zuerst schauen kurze versilberte Kupferdrähte aus der
Oberseite der Platine (oben links), auf denen die Transistoren dann
Platz finden (oben Mitte). Entsprechend wird auch die
SMD-Speicherdrossel eingebaut (oben rechts). Unten: Fertig bestückte
Platine mit Anschlussdrähten
Vor dem Einbau in das Gehäuse
muss der gewünschte LED-Strom über den Trimmer eingestellt werden. Dazu
kommt ein Amperemeter in die Leitung zur LED. Nach dem Anschluss des
geladenen Akkus, jetzt noch ohne Solarzelle, sollte die LED kurz
aufleuchten als Funktionskontrolle und dann erst einmal dunkel bleiben.
Jetzt heißt es Warten auf den verzögerten Zustandswechsel. Nach 5
Minuten sollte sich die LED einschalten. Damit beginnt die Regelung der
Ausgangsspannung. Im Sekunden-Rhythmus steigt der LED-Strom langsam an,
um dann bei einem Maximalwert zu verharren. Am Trimmer lässt sich
dieser Wert verändern. Sinnvolle Werte liegen zwischen 80 und 100 mA.
Allerdings braucht der Einstellprozess etwas Geduld, da die Software
auf jede Änderung in kleinen Schritten reagiert.
Das Gehäuse
besteht aus der um etwa 45 Grad schräg gestellten Solarzelle und
Plastikteilen für Boden, Seiten und Rückwand, die aus einer
Kunststoffplatte aus dem Bastelbedarf zugeschnitten werden. Kleine
Holzleisten dienen zur Stabilisierung. Die Teile werden mit 5-Minuten
Epoxy verklebt, der auch als Dichtung gegen Spritzwasser dient. Die
Solar-LED ist für den längerfristigen Einsatz im Garten gedacht.
Deshalb braucht ein angemessener Schutz vor Regen etwas Aufmerksamkeit.
Dazu werden Elektronik und Akku in Schrumpfschlauch untergebracht. Der
Einbau sollte aber nicht komplett luftdicht sein, um Kondenswasser zu
vermeiden. Zum Schluss wird die Rückwand aufgeschraubt.
Gehäuse mit geöffneter (links) und verschraubter (rechts) Rückwand
Dem
Einsatz im Garten steht jetzt nichts mehr im Wege. Es ist erstaunlich,
wieviel Licht diese kleine LED produziert. Das Schöne an
Solar-getriebenen Leuchten ist, dass sie (hoffentlich) täglich und
klaglos ihren Dienst leisten, ohne weitere Aufmerksamkeit zu benötigen.
Ich wünsche viel Freude mit dem Solar-LED Strahler.
Solar-LED Strahler im Einsatz
Download: solar_led.c
/*
* solar_led.c : Solar LED with ATtiny45
*
* Fuse: CKDIV8 cleared. All other fuses remain at default.
*
* Pin assignment:
* PB0 - pin 5: output controls the power to the solar lamp
* ADC3 (PB3) - pin 2: input from battery pack
* ADC2 (PB4) - pin 3: input from solar panel
* ADC1 (PB2) - pin 7: input from output power
*
* First version: 31-Oct-2016
* Last update: 29-Jul-2017
* Author : Stephan Laage-Witt
*/
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// ADC channels
#define ADC_BATTERY 3
#define ADC_SOLAR 2
#define ADC_OUT 1
// pin definitions
#define BIT_POWER_SWITCH 1
// ADC threshold values. Calulation: ADC value = target_voltage * (33 / 133) * (1024 / 1100)
#define BATT_LOW 692 // threshold to switch LED off: 3.0V
#define BATT_HIGH 808 // threshold to re-enable LED: 3.5V
#define SOL_LOW 230 // solar panel threshold to switch LED on: 1.0V
#define SOL_HIGH 808 // solar panel threshold to switch LED off: 3,5V
#define VOUT_TARGET 650 // approx. 4.5 V
#define ON 255
#define OFF 0
// state definitions
#define STATE_DAY 1
#define STATE_NIGHT 2
#define STATE_BAT_LOW 3
#define STATE_TIME_OUT 4
// timing parameters
#define TIME_OUT 14400 // 4 hours * 60 min * 60 sec
#define SWITCH_DELAY 300 // 5 min * 60 sec
// global variables ----------------------------------------------------------------------------------
volatile uint8_t interrupt_cnt = 0; // interrupt counter
/* interrupt service handler for watchdog timer ------------------------------------------------------*/
ISR(WDT_vect) {
WDTCR |= (1 << WDIE); // wake up CPU and re-enable watchdog interrupt - that's all
}
/* switch_lamp -----------------------------------------------------------------------*
* Switches LED output on or off. Sets OCR0A and PortB / BIT_POWER_SWITCH
* Changes sleep mode: "idle" for lamp on, "power down" for lamp off
* Input: mode (ON or OFF)
*/
void switch_lamp(const uint8_t mode) {
if (mode == ON) {
TCCR0A = (1 << COM0A0) | (1 << COM0A1) | (0 << COM0B0) | (0 << COM0B1); // connect OC0A to compare match, OC0B disconnected (p. 78)
TCCR0A |= (1 << WGM01) | (1 << WGM00); // set OC0A on compare match (data sheet page 101)
TCCR0B = (0 << CS02) | (0 << CS01) | (1 << CS00); // no pre-scaling
OCR0A = 255; // begin without voltage step-up, and increase it from here
PORTB &= ~(1 << BIT_POWER_SWITCH); // switch LED power on
set_sleep_mode(SLEEP_MODE_IDLE); // set sleep mode to "idle", keep timer for PWM running
return;
};
if (mode == OFF) {
PORTB |= (1 << BIT_POWER_SWITCH); // switch power off
TCCR0A = 0; // stop PWM on OCR0A
TCCR0B = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to "power down"
};
}
/* adjust_vout -----------------------------------------------------------------------*
* Sets OCR0A depending on adc_out
* Iterative calls ensure approximation to targeted output voltage
* Input: adc_out - measurement of current output voltage
*/
void adjust_vout(const uint16_t adc_out) {
if (adc_out < VOUT_TARGET) {
if (OCR0A > 120)
OCR0A = OCR0A - 1;
};
if (adc_out > VOUT_TARGET) {
if (OCR0A < 255)
OCR0A = OCR0A + 1;
};
}
/* get_adc --------------------------------------------------------------------------------*
* Reads ADC
* Input: ADC channel
* Returns: ADC value (16 bit, right adjusted)
*/
uint16_t get_adc(const uint8_t adc_channel) {
ADMUX = (1<<REFS1) | adc_channel; // set channel
ADCSRA |= (1<<ADSC); // start ADC conversion
while ((ADCSRA & (1 << ADSC)) > 0); // wait for the end of the conversion
return(ADCW); // return result
}
/* main -----------------------------------------------------------------------------------*/
int main(void)
{
uint8_t current_state = STATE_DAY;
uint16_t time_count = 0, switch_count = 0;
DDRB = 0b11100011; // set OC0A and PB1 to output, and ADC channels to input
PORTB = 0b11100010; // switch off power and pull up resisters
// show that we are alive
_delay_ms(1000);
PORTB &= ~(1 << BIT_POWER_SWITCH); // switch power on
_delay_ms(2000); // keep it on for 2 seconds
switch_lamp(OFF); // initial status: lamp off
// setup watchdog timer
cli(); // disable all interrupts
MCUSR = 0; // clear MCU status register
WDTCR = 0;
WDTCR = (1 << WDIE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0); // setup watchdog for interrupt (not reset), 1 sec cycle time
sei(); // enable interrupts - system is up and running
while (1) {
// initialize ADC
ADMUX = (1<<REFS1) | (1<<REFS0) | (0<<ADLAR); // 10 bit, right adjusted, internal reference 1.1V
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (0<<ADPS0); // scaling factor: 16 -> 62500 kHz
get_adc(ADC_SOLAR); // run the ADC, but ignore the result of the first conversion
switch (current_state) {
case STATE_DAY:
if (get_adc(ADC_SOLAR) < SOL_LOW) {
++switch_count;
if (switch_count >= SWITCH_DELAY) {
current_state = STATE_NIGHT;
time_count = 0;
switch_count = 0;
switch_lamp(ON);
};
} else {
switch_count = 0;
};
break;
case STATE_NIGHT:
if (time_count >= TIME_OUT) {
current_state = STATE_TIME_OUT;
switch_count = 0;
switch_lamp(OFF);
} else if (get_adc(ADC_BATTERY) < BATT_LOW) {
current_state = STATE_BAT_LOW;
switch_count = 0;
switch_lamp(OFF);
} else if (get_adc(ADC_SOLAR) > SOL_HIGH) {
++switch_count;
if (switch_count >= SWITCH_DELAY) {
current_state = STATE_DAY;
switch_count = 0;
switch_lamp(OFF);
};
} else {
switch_count = 0;
};
adjust_vout(get_adc(ADC_OUT));
++time_count;
break;
case STATE_TIME_OUT:
case STATE_BAT_LOW:
if (get_adc(ADC_SOLAR) > SOL_HIGH) {
++switch_count;
if (switch_count >= SWITCH_DELAY) {
current_state = STATE_DAY;
};
} else {
switch_count = 0;
};
break;
};
// shut down ADC to save energy
ADCSRA = (0<<ADEN); // switch off ADC
ADMUX = (0<<REFS1) | (0<<REFS0) | (0<<ADLAR); // switch off internal reference 1.1V
// good night (for one second)
sleep_mode();
}
}
Messung der Batteriespannung von Juergen Schimmer
Ein
sehr hübsches Design - ich hatte jetzt noch eine Idee, den
Ruhestrombedarf noch weiter zu reduzieren und noch einen Pin
freizubekommen - was lassen sich mit einem übrig gebliebenen Pin alles
für Zusatzfunktionen hinzubasteln...). Bei den ATinys lässt sich der
Kehrwert der Versorgungsspannung dadurch messen, dass die
Versorgungsspannung als Referenz verwendet wird ( REFS[2:0] = 000 ) und
die Referenzspannung über den Multiplexer an den Eingang des ADC gelegt
wird. ( Mux[3:0] = 1100 ). Damit wird der Pin2 ( ADC3 ) frei, und
je ein 33k und 100k Widerstand können entfallen. Damit sinkt der
Ruhestrombedarf nochmals um fast 3uA. Jetzt ergibt sich nur noch das
Problem - was machen mit dem freien Portpin - er ruft ja geradezu nach
einer Aufgabe...
Elektronik-Labor Projekte AVR