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)
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.
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--;
}
}
Auch der Neigungsschalter hat sich als wirklich empfindlicher Impulsgeber erwiesen, denn es braucht nur eine minimale Erschütterung, und die LED leuchtet auf.
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--;
}
}
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.
#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--;
}
}
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.
#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