home
erste Version am 21.11.2017
letzte Änderung am 05.12.2017

LED-Streifen als Nachtlicht


Bei diesem Projekt soll es um eine Treppenhaus-Beleuchtung gehen, die nur bei Dunkelheit und erkannter Bewegung gerade soviel Licht bereitstellt, dass man die Treppe auch Nachts noch sicher benutzen kann - speziell, wenn gerade mal wieder Dinge auf der Treppe gelagert werden.
Bisher hatte ich dazu ein Akku-betriebenes Modell von Aldi im Einsatz. Das hat aber folgende Schwächen: Ziel soll sein, diese Schwächen abzustellen.

Jetzt habe ich mir vier unterschiedliche, jeweils 5m lange LED-Streifen bestellt. Einer davon soll an der Unterseite des Treppengeländers bzw. Handlaufs montiert werden.
Ein Set mit fünf Bewegungsmeldern hatte ich mir Anfang des Jahres schon mal zum Spielen bestellt und ein paar LDRs werde ich auch noch rumliegen haben.
Die Erkennung der Einschaltbedingung und die Ansteuerung der LEDs soll mittels ATmega328 erfolgen. Das ist zwar garantiert Overkill....aber von dem Baustein habe ich zuhauf Exemplare bevorratet.

Außerdem schwebt mir vor, die LEDs per PWM so anzusteuern, dass deren Helligkeit von der Umgebungs-Helligkeit abhängt. Idealerweise sollte die reguläre Treppenhaus-Beleuchtung gar nicht mehr [manuell] eingeschaltet werden müssen. Also volle LED-Helligkeit bei Schummerlicht und bei völliger Dunkelheit gerade soviel LED-Helligkeit, dass man potentielle Hindernisse noch gut erkennen kann - ebenso wie die erste und die letzte Treppenstufe.


Inhalt

Bewegungsmelder (HC-SR501)
erster Entwurf
    Test
zweiter Entwurf
    Test
Helligkeits-Justierung
Testaufbau mit ArduinoUNO
Anpassung des Testaufbaus
Fototransistor statt Fotowiderstand
Bewegungsmelder - die Zweite
Helligkeits-Beziehung
Aufbau der Schaltung
Firmware
neuer Ansatz
erster Einsatz
Logging über WLAN
Helligkeits-Anpassung
zurück auf Los
Bau weiterer Nachtlichter
das erste Nachtlicht ist produktiv
das zweite Nachtlicht
das dritte Nachtlicht
gegenseitige Beeinflussung


Bewegungsmelder (HC-SR501)

Der vorgesehene Bewegungsmelder arbeitet zwischen 4,5V bis 20V Gleichspannung und hat drei Kontakte für Vcc, OUT und GND.
Ohne erkannte Bewegung gilt OUT=GND.
Sobald Bewegung erkannt wurde, gilt OUT=3.3V. Und zwar für mindestens 3 Sekunden.
Wird innerhalb dieser 3 Sekunden weiterhin Bewegung erkannt, bleibt OUT=3.3V.
Wurde jedoch nach OUT=3.3V auf OUT=GND umgeschaltet, erfolgt für die folgenden 3 Sekunden keine Bewegungserkennung mehr.
Praktisch sollte dieses Verhalten nicht stören, wenn die LEDs ohnehin immer für mindestens 30 Sekunden eingeschaltet bleiben.

Zwar werde ich nicht umhin kommen, ein 12V-Netzteil dauerhaft unter Saft zu haben, aber der ATmega328 soll erst dann Strom ziehen, wenn Bewegung erkannt wurde. Nach PowerOn würde der ATmega328 zunächst den LDR abfragen und die LEDs nur einschalten, wenn es dunkel genug ist.
Die LEDs blieben nach der letzten Bewegungs-Erkennung für eine definierte Zeitdauer eingeschaltet - danach würden sie per PWM langsam ausgedimmt.


erster Entwurf

Der entsprechende Schaltplan sieht im ersten Entwurf so aus:
Nachtlicht-Schaltplan-V1

Der Bewegungsmelder soll laut der Produktbeschreibung auf Amazon einen Ruhe-Strom von <50µA haben, der Rest meiner Schaltung sollte nur dann Strom ziehen, wenn der Bewegungsmelder am Pin-2 HIGH meldet. Den meisten Strom wird also das Netzteil für sich selbst verbrauchen. Dementsprechend ist ein Modell mit möglichst geringem Standby-Strom zu wählen.
Das bei diesem LED-Streifen mitgelieferte Netzteil zieht im Leerlauf 1,3W - obwohl es als A+ spezifiziert ist und laut Aufdruck nur kümmerliche 0,6A bzw. 7,2W liefert.
Dies hier wird mit "No load power consumption<0.3W" angegeben und ist damit schon eher nach meinem Geschmack.
Mit seinen 45W kann es also leicht fünf Meter LED-Streifen versorgen. Zwei Treppen sollen beleuchtet werden, jede Treppe hat drei jeweils zwei Meter lange Handläufe. Pro Treppe soll mindestens der jeweils mittlere Handlauf mit einem LED-Streifen ausgestattet werden.
Also zwei je zwei Meter lange LED-Streifen bzw. Strom für vier Meter LEDs. Wobei ich dann, wenn ich die LEDs beider Etagen aus einem Netzteil speisen will, die 12V-Leitung durch den Flur im Erdgeschoss verlegen muss.... Das heißt: hässliche Kabelkanäle. Aber unterputz kommt nicht in Frage.
Dieses 12V-Netzteil ist mit "Standby-Energieaufnahme: ~ 0,5W" angegeben, liefert sogar 60W bzw. 5A und kommt mit Gehäuse samt Zuleitungen. Damit sollte es für meinen Einsatzzweck geeigneter sein. Schließlich will ich die paar Bauteile lieber in ein kleines Gehäuse einbauen (in dem auch der Bewegungssensor sitzt), das statt einer 230V- nur eine 12V-Zuleitung bekommt und irgendwo unterhalb vom Treppengeländer strategisch montiert wird.

Test

Eben habe ich den linken Teil der Schaltung (bis zum Regler) auf einem Breadboard zusammengesteckt.
Erste Erkenntnis: mit 100KΩ am Gate des MOSFETs klappt es nicht. Nehme ich stattdessen 10KΩ, klappt es zuverlässig, dass nur bei erkannter Bewegung am Regler-Ausgang 5V anliegen.
Zweite Erkenntnis: der ATmega328 wird noch eine Verbindung zum Pin-2 des Bewegungssensors brauchen, wenn die LEDs 30 Sekunden länger eingeschaltet bleiben sollen, nachdem die letzte Bewegung erkannt wurde. Nur mit einer solchen Verbindung kann der ATmega328 erkennen, dass der Bewegungssensor erneut geschaltet hat. In diesem Fall wären die 30 Sekunden Nachlauf neu zu starten.

Weiterhin stellt sich die Frage, was der rechte BC338 und der MJE3055 wohl machen werden, wenn der ATmega328 stromlos ist.
Und die nächste Frage ist, wie ich das überhaupt komfortabel mit einem ArduinoUNO testen soll - wenn der seine 5V ja über USB vom Rechner bezieht. Eine temporär angeklemmte LED hinter dem Regler könnte helfen. Und für die zwei Transistoren könnte ich vielleicht je einen Optokoppler spendieren. Also so, dass der BC338 durch einen Optokoppler ersetzt und der MJE3055 über einen Optokoppler angesteuert wird. Damit wären die 12V dann gleichzeitig vernünftig von den 5V getrennt. Das kann jedenfalls nicht schaden und ist sicher gesünder für den stromlosen ATmega328.


zweiter Entwurf

Alternativ kann man den Schaltplan insofern vereinfachen, dass die 5V für den ATmega328 dauerhaft verfügbar sind:
Nachtlicht-Schaltplan-V1
Laut Datenblatt zieht ein 7805 im Leerlauf typisch 5mA und maximal 8mA.
Würde der ATmega328 über den Bewegungsmelder aus dem Tiefschlaf geweckt werden, bliebe es die meiste Zeit bei diesen 5mA.
Entsprechender Programmkode findet sich hier unter der Überschrift "Waking from sleep with a signal".
Neben der Bauteil-Einsparung hätte diese Version den weiteren Vorteil, dass sich Schaltung und Programm problemlos mit einem ArduinoUNO testen ließe.

Andererseits brauche ich die Schaltung samt beider Sensoren pro Treppe. Somit 10mA Leerlauf-Strom.
Oder ein zentraler 5V-Regler und dreiadriges Kabel verlegen. Für diesen Zweck habe ich aber schon 25m Lautsprecherkabel (0,5mm2) bestellt - also zweiadrig.
Das hat pro Litze 1,7Ω auf die kompletten 25 Meter. Wobei das Multimeter bereits 0,5Ω anzeigt, wenn ich die Messleitungen direkt verbinde. Dementsprechend hat die Litze offenbar 1,7Ω-0,5Ω=1,2Ω auf 25 Meter.

Würde der ATmega328 seinen Maximalstrom von 200mA ziehen, ergäbe das für die Leitung einen Spannungsabfall von 1,2Ω*0,2A=0,24V. Auch wenn der ATmega328 noch problemlos mit 4,76V laufen sollte, wird er sich möglicherweise daran stören, dass seine Versorgungsspannung je nach Stromaufnahme um 0,24V schwankt. Schließlich zieht er im Tiefschlaf nur etwa 10µA und das führt folglich zu 1,2Ω*0,00001A=0,000012V=12µV Spannungsabfall auf der Leitung.

Messe ich hingegen mein ebenfalls 25 Meter langes Rasenmäher-Kabel durch, bekomme ich dafür einen Widerstand von 0,8Ω angezeigt. Abzüglich der 0,5Ω für die Messleitungen also 0,3Ω. Ein größerer Leitungsquerschnitt könnte damit eine mögliche Option sein (0,3Ω*0,2A=0,06V). Vielleicht tut es ein dicker Elko ebenso.

Test

Nun teste ich zunächst mal das Aufwachen aus dem Tiefschlaf bei Bewegungserkennung.
Dazu wird der Bewegungsmelder vom ArduinoUNO mit 5V versorgt, sein Signal-Pin ist mit Pin 2 des ArduinoUNO verbunden.
Ebenfalls ist der LDR samt 10KΩ-Widerstand verbaut und am AnalogPin 5 angeschlossen. Programmkode ist folgender:
#include <avr/sleep.h>

#define MOTION_PIN 2
#define LDR_PIN 5

#define AWAKE_SECS_AFTER_MOTION 10


/* -------------------------------------------------------------------
* ADC wieder aktivieren.
* https://www.gammon.com.au/adc
*/
void restore_ADC(void) {
ADCSRA=bit(ADEN); // turn ADC on
ADCSRA|=bit(ADPS0)|bit(ADPS1)|bit(ADPS2); // Prescaler of 128
}

/* -------------------------------------------------------------------
* Wird bei LOW-HIGH-Flanke an Pin2 aufgerufen.
* Quelle: http://www.gammon.com.au/power ( Sketch J)
*/
void wake() {
sleep_disable();
detachInterrupt(0);
restore_ADC();
Serial.println("aufgewacht"); delay(10);
}

/* -------------------------------------------------------------------
* CPU in den Tiefschlaf-Modus versetzen.
* Aufwachen durch LOW-HIGH-Flanke an Pin2 bzw. Int0.
*/
void deep_sleep(void) {
Serial.println("Tiefschlaf"); delay(10);
ADCSRA=0; // disable ADC
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();

// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts();

// will be called when pin D2 goes high
attachInterrupt(0, wake, RISING);
EIFR=bit(INTF0); // clear flag for interrupt 0

// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR=bit(BODS)|bit(BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR=bit(BODS);

// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts(); // one cycle
sleep_cpu(); // one cycle
}

/* -------------------------------------------------------------------
* Initialisierung nach PowerOn.
*/
void setup() {
Serial.begin(115200);
pinMode(MOTION_PIN, INPUT);
}

/* -------------------------------------------------------------------
* Mainloop.
*/
void loop() {
static int secs_after_motion=0;

Serial.print("(");
Serial.print(secs_after_motion);
Serial.print(", ");
Serial.print(digitalRead(MOTION_PIN));
Serial.print(", ");
Serial.print(analogRead(LDR_PIN));
Serial.print("), ");

if(digitalRead(MOTION_PIN)==LOW) {
if(secs_after_motion>=AWAKE_SECS_AFTER_MOTION) {
secs_after_motion=0;
deep_sleep();
Serial.println("nach deep_sleep()"); delay(10);
}
} else {
secs_after_motion=0;
}
secs_after_motion++;
delay(1000);
}

Damit wird im Terminal sowas hier geliefert:
(0, 0, 543), (1, 0, 545), (2, 0, 549), (3, 0, 559), (4, 0, 571), (5, 0, 577), (6, 0, 579), (7, 0, 580), (8, 0, 578), (9, 0, 575), (10, 0, 570), Tiefschlaf
aufgewacht
nach deep_sleep()
(1, 1, 539), (1, 1, 545), (1, 1, 543), (1, 1, 213), (1, 1, 187), (1, 0, 191), (2, 0, 188), (3, 0, 191), (4, 0, 195), (5, 0, 202), (6, 0, 199), (7, 0, 520), (8, 0, 536), (9, 0, 547), (10, 0, 555), Tiefschlaf
aufgewacht
nach deep_sleep()
(1, 1, 562), (1, 1, 550), (1, 1, 542), (1, 1, 546), (1, 0, 557), (2, 0, 554), (3, 0, 550), (4, 0, 543), (5, 0, 537), (6, 0, 537), (7, 0, 539), (8, 0, 543), (9, 0, 550), (10, 0, 560), Tiefschlaf

Ergebnis: klappt wie vorgesehen.
In der zweiten Wach-Phase habe ich den LDR kurz mal abgedunkelt, um so die Funktion des AD-Wandlers zu prüfen. Immerhin war der im Tiefschlaf abgeschaltet.


Helligkeits-Justierung

Als nächstes muss ich mir überlegen, wie ich die Werte der Relation "Umgebungs-Helligkeit zu LED-PWM-Wert" bestimmen kann.
Wahrscheinlich sollten drei oder vier Stufen reichen. Also etwa so:
LDR-Wert
PWM-Wert für die LEDs
>400 - Tageslicht
0 - LEDs aus
100 - 400 - Zwielicht
255 - LEDs maximal hell
2 - 99 - Schummerlicht
50 - LEDs halbwegs hell
<2 - Dunkelheit
5 - LEDs ziemlich dunkel
Außer den beiden oberen PWM-Werten sind alle Zahlenwerte geschätzt und müssen am Installationsort justiert werden.
Vielleicht könnte ich einen Port für einen temporär anschließbaren Drehgeber samt Schalter vorsehen.

Vom Programmier-Aufwand her wäre es natürlich deutlich einfacher, die Beziehung der LDR- und LED-Helligkeitswerte in der Firmware statisch festzulegen.
Die dazu nötige Testreihe ließe sich ggf. sogar am finalen Installationsort durchführen, wenn die Schaltung temporär von einem ArduinoUNO gesteuert würde, der seinerseits mit einem Laptop verbunden wäre.

Und zu den Stufen....einige wenige Helligkeitsstufen hätten den Nachteil, dass bereits eine minimale Änderung beim LDR-Wert ggf. zu einer sehr deutlichen Änderung bei der LED-Helligkeit führen könnte. Dann nämlich, wenn die LDR-Helligkeit zufällig genau auf der Grenze zwischen zwei Stufen läge.
So gesehen sollte die LED-Helligkeit wohl doch lieber über eine lineare Funktion vom LDR-Wert abgeleitet werden. Mittels map() zum Beispiel.

Der LDR wäre idealerweise so zu positionieren, dass er seinen Widerstand nicht ändert, wenn die LEDs eingeschaltet werden.
Andererseits kann man dessen Wert auch einfach einmalig vor dem Einschalten der LEDs abfragen.


Testaufbau mit ArduinoUNO

Zum Bestimmen der Helligkeitsbeziehungen könnte der Testaufbau also etwa folgendermaßen aussehen:
Testaufbau mit ArduinoUNO
Als Schaltplan entsprechend:
Schaltplan mit ArduinoUNO

Wegen der komischen Pin-Nummerierung vom Leistungstransistor im Schaltplan hier noch dessen Pinbelegung laut Datenblatt.....und wo ich gerade dabei bin, auch gleich noch die des 5V-Spannungsreglers, der in der nächsten Ausbaustufe zum Einsatz kommen wird:
MJE3055     7805
Mit dieser Version 1.0 des Firmware-Sourcecodes funktioniert die Schaltung bereits so, wie ich mir das vorgestellt habe.
Den analogRead() habe ich abweichend zum Schaltplan nachträglich auf A0 statt A5 gelegt - für den Fall, dass ich irgendwann vielleicht mal auf die Idee kommen könnte, noch Irgendetwas via I2C anschließen zu wollen.

Auch wenn eigentlich schon alles klappt, gefällt mir das noch nicht so wirklich.
Ich messe nämlich nur 10,5V (statt der möglichen 12,3V) über den LEDs. Obwohl der PWM-Pin 11 per analogWrite() auf dem Wert 255 steht.
So geht das nicht....damit geht es auf der nächsten Seite weiter.