Ausschaltverzögerung für eine LED IV. (digital)

In den folgenden Experimenten soll eine LED durch einen externen Impuls eingeschaltet werden, eine gewisse Zeit von alleine weiterleuchten und dann von alleine wieder ausgehen. Wird während der Leuchtphase der Impuls erneut ausgelöst, wird die Zeit die Leuchtdauer zurückgesetzt und beginnt erneut. Als Auslöser werden sowohl ein (kleiner) Gleichstrom-Motor als auch ein Neigungsschalter (Tilt-Schalter) verwendet. Die gesamte Logik soll auf einen ATtiny85-Mikrocontroller programmiert werden, damit die gesamte Schaltung während der Dunkelphasen möglichst wenig Strom verbraucht. (Hier kommt der Sleep-Modus zum Einsatz)

Verwendete Bauteile

DC-Motor als Auslöser

Ein Wind- oder Wasserrad könnte einen kleinen DC-Motor betreiben und somit den Impuls zum Einschalten der LED geben.
Nachdem ich einige verschiedene DC-Motoren durchprobiert hatte, erwiesen sich die meisten davon als recht empfindliche Auslöser, d.h. es ist nur eine sehr kurze Drehbewegung notwendig, um ein Aufleuchten der LED auszulösen.

Aufbau der Schaltung

Aufbau der Schaltung mit dem DC-Motor als Auslöser

Sketch

Ein vorgegebener Timer (hier: 10 Sekunden) wird gesetzt, sobald der PIN des Sensors reagiert. Dabei wird die LED eingeschaltet. Nun zählt der Timer bis 0 herunter und lässt dann die LED wieder erlöschen. Wird während der Zeit des Herunterzählens erneut der Trigger-PIN ausgelöst, so beginnt der Timer erneut von vorne.
Im folgenden Sketch können die Werte für SENSITIVITY (=Empfindlichkeit des Eingangs-Signals) und LIGHT_TIME (=Leuchtdauer der LED) auf die eigenen Bedürfnisse angepasst werden. Der Sketch wurde als Test nur für den Arduino Uno geschrieben und muss für den ATtiny angepasst werden (z.B. entfällt hierbei Serial)

#define PIN_LED     4
#define PIN_TRIGGER A0

#define SENSITIVITY      5  // [0..1023]
#define LIGHT_TIME      10  // in s

#define CHECK_INTERVAL 100  // in ms
#define ONE_SECOND    1000  // in ms

#define lmillis() ((long)millis())

bool isLedLit;
int ledTimer = 0;
long lastSensorCheck = 0, lastCounterAction = 0;

void setup()
{
    Serial.begin(9600);

    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);
    isLedLit = false;

    lastSensorCheck = lmillis() + CHECK_INTERVAL;
    lastCounterAction = lmillis() + LIGHT_TIME;
}

void loop()
{
    if (isLedLit && ledTimer <= 0) {
        Serial.println("Timeout");
        digitalWrite(PIN_LED, LOW);
        isLedLit = false;
    }

    if (lmillis() - lastSensorCheck >= 0) {
        lastSensorCheck = lmillis() + CHECK_INTERVAL;
        if (analogRead(PIN_TRIGGER) > SENSITIVITY) {
            Serial.println("Reset timer - " + String(analogRead(PIN_TRIGGER)));
            digitalWrite(PIN_LED, HIGH);
            isLedLit = true;
            ledTimer = LIGHT_TIME;
        }
    }

    if (ledTimer > 0 && lmillis() - lastCounterAction >= 0) {
        lastCounterAction = lmillis() + ONE_SECOND;
        Serial.println(ledTimer);
        ledTimer--;
    }
}

Neigungsschalter als Auslöser

Auch der Neigungsschalter hat sich als wirklich empfindlicher Impulsgeber erwiesen, denn es braucht nur eine minimale Erschütterung, und die LED leuchtet auf.

Aufbau der Schaltung

Aufbau der Schaltung mit Neigungsschalter als Auslöser

Sketch

Der folgende Sketch ist für den Arduino Uno zugeschnitten und reagiert beim Neigungsschalter auf jegliche Zustandsänderung, d.h. nicht nur von aus zu ein, sondern auch umgekehrt. Wiederrum lässt sich die Leuchtdauer mit LIGHT_TIME beliebig anpassen.

#define PIN_LED     4
#define PIN_TRIGGER 2

#define LIGHT_TIME   10 // in s
#define ONE_SECOND 1000 // in ms

#define lmillis() ((long)millis())

bool isLedLit;
byte tiltStatus = LOW, lastTiltStatus = LOW;
int ledTimer = 0;
long lastAction = 0;

void setup()
{
    Serial.begin(9600);

    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);
    isLedLit = false;

    pinMode(PIN_TRIGGER, INPUT_PULLUP);
    lastAction = lmillis() + LIGHT_TIME;
}

void loop()
{
    if (isLedLit && ledTimer <= 0) {
        Serial.println("Timeout");
        digitalWrite(PIN_LED, LOW);
        isLedLit = false;
    }

    tiltStatus = digitalRead(PIN_TRIGGER);
    if (tiltStatus != lastTiltStatus && ledTimer < LIGHT_TIME) {
        lastTiltStatus = tiltStatus;
        Serial.println("Reset timer");
        digitalWrite(PIN_LED, HIGH);
        isLedLit = true;
        ledTimer = LIGHT_TIME;
    }

    if (ledTimer > 0 && lmillis() - lastAction >= 0) {
        lastAction = lmillis() + ONE_SECOND;
        Serial.println(ledTimer);
        ledTimer--;
    }
}

Vom Arduino zum ATtiny

Zur Verkleinerung des gesamten Aufbaus habe ich den Sketch entsprechend für einen ATtiny85 angepasst. Außer dass die serielle Konsole Serial wegfiel und die verwendeten Pins angepasst werden mussten, ist der Sketch gleich geblieben.

Aufbau der Schaltung

Aufbau der Schaltung mit dem ATtiny85

Sketch für den ATtiny85

#define PIN_LED     PCINT0
#define PIN_TRIGGER PCINT1

#define LIGHT_TIME   10 // in s
#define ONE_SECOND 1000 // in ms

#define lmillis() ((long)millis())

bool isLedLit;
byte tiltStatus = LOW, lastTiltStatus = LOW;
int ledTimer = 0;
long lastAction = 0;

void setup()
{
    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);
    isLedLit = false;

    pinMode(PIN_TRIGGER, INPUT_PULLUP);
    lastAction = lmillis() + LIGHT_TIME;
}

void loop()
{
    if (isLedLit && ledTimer <= 0) {
        digitalWrite(PIN_LED, LOW);
        isLedLit = false;
    }

    tiltStatus = digitalRead(PIN_TRIGGER);
    if (tiltStatus != lastTiltStatus && ledTimer < LIGHT_TIME) {
        lastTiltStatus = tiltStatus;
        digitalWrite(PIN_LED, HIGH);
        isLedLit = true;
        ledTimer = LIGHT_TIME;
    }

    if (ledTimer > 0 && lmillis() - lastAction >= 0) {
        lastAction = lmillis() + ONE_SECOND;
        ledTimer--;
    }
}
Prototyp der Ausschaltverzögerung mit ATtiny85 und Neigungsschalter auf dem Breadboard (2,4MB).

Sleep-Modus zum Strom sparen

Da in den Intervallen, in denen die LED nicht leuchtet, eigentlich der Mikrocontroller nicht viel mehr tut, als auf einen externen Auslöser zu warten, war es durchaus sinnvoll, den Sleep-Modus während dieser Phasen einzuschalten, um Strom zu sparen.
Bei einer Spannungsversorgung von 3V (Knopfbatterie CD2032) verbraucht die Schaltung im normalen Betrieb ca. 1,74mA (LED ein) bzw. 1,15mA (LED aus), im Sleep-Modus jedoch nur noch ~0,1mA.

Sketch

#include <avr/sleep.h>
#include <avr/interrupt.h>

#define PIN_LED     PCINT0 // =PB0 (Pin #5)
#define PIN_TRIGGER PCINT1 // =PB1 (Pin #6)

#define LIGHT_TIME   60 // in s
#define ONE_SECOND 1000 // in ms

#define lmillis() ((long)millis())

bool isLedLit;
byte tiltStatus = LOW, lastTiltStatus = LOW;
int ledTimer = 0;
long lastAction = 0;

// This is called when the interrupt occurs, but I don't need to do anything in it
ISR(PCINT0_vect) {}

void enterSleepMode(void)
{
    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PIN_TRIGGER);
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PIN_TRIGGER);             // Turn off PB3 as interrupt pin
    sleep_disable();                        // Clear SE bit
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
}

void setup()
{
    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);
    isLedLit = false;

    pinMode(PIN_TRIGGER, INPUT_PULLUP);
    lastAction = lmillis() + LIGHT_TIME;
}

void loop()
{
    if (isLedLit && ledTimer <= 0) {
        digitalWrite(PIN_LED, LOW);
        isLedLit = false;
        enterSleepMode();
    }

    tiltStatus = digitalRead(PIN_TRIGGER);
    if (tiltStatus != lastTiltStatus && ledTimer < LIGHT_TIME) {
        lastTiltStatus = tiltStatus;
        digitalWrite(PIN_LED, HIGH);
        isLedLit = true;
        ledTimer = LIGHT_TIME;
    }

    if (ledTimer > 0 && lmillis() - lastAction >= 0) {
        lastAction = lmillis() + ONE_SECOND;
        ledTimer--;
    }
}
zurück