Hallo an die Gemeinschaft.
Ich möchte mit einem Arduino Pro Micro eine LED- Kette für 6 Stunden ein und für 18 Stunden ausschalten. Nach Ablauf der 24 Stunden soll die LED-Kette wieder an gehen.
Ich habe das folgende Prgramm geschrieben.
Die LED geht nach dem Start über den Taster an Pin 2 an, aber sie geht nicht wieder aus.
Kann mir jemand bei der Programmerstellung helfen.
Danke im Voraus.
//PROGRAMM ohne delay()
//Arduino Pro Micro
#include <avr/sleep.h>
#include <avr/wdt.h>
#define BUTTON_PIN 2 // Taster an Pin 2
#define OUTPUT_PIN 3 // Ausgang an Pin 3
volatile bool buttonPressed = false;
unsigned long previousMillis = 0; // Speichert die letzte Zeit
const unsigned long onDuration = 21600000; // 6 Stunden in Millisekunden
const unsigned long offDuration = 64800000; // 18 Stunden in Millisekunden
unsigned long durationCounter = 0; // Zähler für die Zeit
enum State {
ON,
OFF
};
State currentState = OFF;
void setup() {
pinMode(OUTPUT_PIN, OUTPUT); // Setze den Ausgangspin als Ausgang
pinMode(BUTTON_PIN, INPUT_PULLUP); // Setze den Taster-Pin als Eingang mit Pull-Up-Widerstand
// Interrupt für den Taster aktivieren
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
}
void loop() {
if (buttonPressed) {
buttonPressed = false;
currentState = ON; // Setze den Zustand auf ON
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang ein
previousMillis = millis(); // Setze die Startzeit
}
if (currentState == ON) {
durationCounter = millis() - previousMillis; // Berechne die vergangene Zeit
if (durationCounter >= onDuration) { // Wenn die Zeit für ON abgelaufen ist
digitalWrite(OUTPUT_PIN, LOW); // Schalte den Ausgang aus
currentState = OFF; // Setze den Zustand auf OFF
previousMillis = millis(); // Setze die Startzeit für OFF
}
} else if (currentState == OFF) {
durationCounter = millis() - previousMillis; // Berechne die vergangene Zeit
if (durationCounter >= offDuration) { // Wenn die Zeit für OFF abgelaufen ist
currentState = ON; // Setze den Zustand auf ON zurück
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang wieder ein
previousMillis = millis(); // Setze die Startzeit erneut
} else {
enterSleepMode(); // Gehe in den Sleep-Modus während der OFF-Zeit
}
}
}
void enterSleepMode() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Setze Sleep-Modus auf Power Down
sleep_enable(); // Aktiviere Sleep-Modus
sleep_cpu(); // Gehe in den Sleep-Modus
sleep_disable(); // Deaktiviere Sleep-Modus nach dem Aufwachen
wdt_reset(); // WDT zurücksetzen, um einen Reset zu vermeiden
}
// Interrupt-Service-Routine für den Taster
void buttonISR() {
buttonPressed = true;
}
//PROGRAMM ohne delay()
//Arduino Pro Micro
#define BUTTON_PIN 2 // Taster an Pin 2
#define OUTPUT_PIN 3 // Ausgang an Pin 3
volatile bool buttonPressed = false;
unsigned long previousMillis = 0; // Speichert die letzte Zeit
const unsigned long onDuration = 21600000; // 6 Stunden in Millisekunden
const unsigned long offDuration = 64800000; // 18 Stunden in Millisekunden
unsigned long durationCounter = 0; // Zähler für die Zeit
enum State {
ON,
OFF
};
State currentState = OFF;
void setup() {
pinMode(OUTPUT_PIN, OUTPUT); // Setze den Ausgangspin als Ausgang
pinMode(BUTTON_PIN, INPUT_PULLUP); // Setze den Taster-Pin als Eingang mit Pull-Up-Widerstand
// Interrupt für den Taster aktivieren
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
}
void loop() {
if (buttonPressed) {
buttonPressed = false;
currentState = ON; // Setze den Zustand auf ON
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang ein
previousMillis = millis(); // Setze die Startzeit
}
durationCounter = millis() - previousMillis; // Berechne die vergangene Zeit
if (currentState == ON) {
if (durationCounter >= onDuration) { // Wenn die Zeit für ON abgelaufen ist
digitalWrite(OUTPUT_PIN, LOW); // Schalte den Ausgang aus
currentState = OFF; // Setze den Zustand auf OFF
previousMillis = millis(); // Setze die Startzeit für OFF
}
} else {
if (durationCounter >= offDuration) { // Wenn die Zeit für OFF abgelaufen ist
currentState = ON; // Setze den Zustand auf ON zurück
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang wieder ein
previousMillis = millis(); // Setze die Startzeit erneut
}
}
}
// Interrupt-Service-Routine für den Taster
void buttonISR() {
buttonPressed = true;
}
Liest sich wie Terrarium, Aquarium oder dergleichen. Dann soll das vermutlich über eine längere Zeit laufen, wozu die Schwingungen eines µCs, auf denen die millis() basieren, leider zu ungenau sind.
Stattdessen gibt es temperaturkompensierte Uhrenmodule wie DS3231, die im Jahr ein paar Sekunden Abweichung haben. Noch genauer geht es mit der Zeit aus dem WWW oder von der Fritz!Box (Router).
Ich möchte es nur erwähnt haben, denn Arduinos können das
Es soll tatsächlich nur eine LED-Lichterkette angesteuert werden. Wobei es über eine Batterie gespeist wird und möglichst klein sein soll.
Wie groß wird die Zeitabweichung ca. pro 24 Stunden? Kann man das abschätzen.
Schwierig, da es sich um Fertigungstoleranzen und Umgebungsbedingungen wie die Temperatur handelt, die Einfluß nehmen.
Aber Du kannst es selbst testen, indem Du ein Programm schreibst, daß Dir jede Minute oder so die millis() rausschreibt. Dann vergleichst Du das mit der Uhrzeit, die der serielle Monitor mit anzeigen kann. Da sollte sich über den Tag eine Abweichung ergeben.
Warum einfach, wenn es auch klein gehen könnte.
Betrieb, wie gesagt, über 2 Lipos, daher eigentlich an die Möglichkeit geglaubt, den Micro während der "OFF-Zeiten" vo Pin 3, in den Stromsparmodus zu schicken.
Programm soll natürlich ca 6 Wochen laufen.
Es wäre schön, die Ursache herauszufinden. (Auf den ersten Blick sehe ich kein Problem, jedenfalls bis zum ersten Ausschalten)
Vermutlich fängst du dir durch Störung/falsche Verdrahtung öfters mal ein Interrupt ein, der die LED immer wieder neu startet.
Hast du das ganze mal mit kürzerer Zeit getestet?
In den if(buttonPressed) Zweig eine Testausgabe eingebaut?
//PROGRAMM ohne delay()
//Arduino Pro Micro
#define BUTTON_PIN 2 // Taster an Pin 2
#define OUTPUT_PIN 3 // Ausgang an Pin 3
volatile bool buttonPressed = false;
unsigned long previousMillis = 0; // Speichert die letzte Zeit
const unsigned long onDuration = 21600000; // 6 Stunden in Millisekunden
const unsigned long offDuration = 64800000; // 18 Stunden in Millisekunden
unsigned long durationCounter = 0; // Zähler für die Zeit
enum State
{
ON,
OFF
};
State currentState = OFF;
void setup()
{
pinMode(OUTPUT_PIN, OUTPUT); // Setze den Ausgangspin als Ausgang
pinMode(BUTTON_PIN, INPUT_PULLUP); // Setze den Taster-Pin als Eingang mit Pull-Up-Widerstand
// Interrupt für den Taster aktivieren
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
}
void loop()
{
durationCounter = millis() - previousMillis; // Berechne die vergangene Zeit
if (digitalRead(OUTPUT_PIN) == HIGH)
{
if (durationCounter >= onDuration) // Wenn die Zeit für ON abgelaufen ist
{
digitalWrite(OUTPUT_PIN, LOW); // Schalte den Ausgang aus
previousMillis = millis(); // Setze die Startzeit für OFF
}
}
else // Wenn AUS
{
if (durationCounter >= offDuration) // Wenn die Zeit für OFF abgelaufen ist
{
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang wieder ein
previousMillis = millis(); // Setze die Startzeit erneut
}
if (buttonPressed) // Prüfung ob ausgelöst
{
buttonPressed = false;
digitalWrite(OUTPUT_PIN, HIGH); // Schalte den Ausgang ein
previousMillis = millis(); // Setze die Startzeit
}
}
}
// Interrupt-Service-Routine für den Taster
void buttonISR()
{
buttonPressed = true;
}
Damit löst der Button nur aus, wenn die LED auch wirklich aus ist.
Jetzt oute ich mich mal (ungewollt)
Ich hab noch nie was mit dem Watchdog gemacht.
Bitte schaut jemand drüber und korrigiert mich.
Der Wachtdog erzeugt einen Hardwarereset falls innerhalb der programmierten Zeit (max 4 Sekunden ) kein Zurücksetzen des Watchdog erfolgt. Das zurücksetzen erfolgt durch die (vorhandene) Funktion wdt_reset(); welche aber an ganz der falschen Stelle sitzt. Die richtige Stelle müßte ohne IF-Kondition im loop() sein. Es fehlt die Angabe des watchdogsintervall im Setup.
setup() {
wdt_disable();
... Restlicher Code im setup()
wdt_enable(WDTO_2S);
}
Darum hat der watchdog nicht die erwartete Funktion und könnte sogar den Arduino in einer Watchdogschleife halten.
Zweite Frage.
Im Sleepmodus wird millis überhaupt weitergezählt?
Das ist aber kein Problem, weil das Programm, so wie vom To geschrieben nach einem Tastendruck ( Tasten sollte man nicht mittels Interrupt abfragen) zuerst 6 Stunden an ist und dann 18 Stunden Aus um dann in sleep / watchdog Modus zu fallen.
Ich frage mich ob der Watchdog überhaupt so funktioneirt da ansonsten der Arduino in diesem Sketch in einem Dauerresetoszillation gehalten würde.
Hier ist eine einfache Variante um einen ATMega368 per Watchdog im Sleepmodus zu steuern. Der Watchdog wird auf den Interruptmodus eingestellt und die Periode auf die längst mögliche Spanne von 8 Sekunden.
Im vorliegenden Beispiel wird etwas in loop() gemacht und anschließend in einer Schleife der Sleepmodus angeschaltet. Mit jedem Aufwachen nach 8 Sekunden wird die Schleife einmal durchlaufen und der Controller so lange gleich wieder schlafen gelegt, bis die gewünschte Schlafenszeit (x * 8 Sekunden) abgelaufen ist. Daraufhin wird die loop Funktion erneut aufgerufen.
Resettet wird da nichts.
#include <Arduino.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <util/atomic.h>
constexpr byte WATCHDOG_TIME {8}; // in Sekunden
constexpr uint32_t SLEEP_ZEIT {32};
// watchdog ISR
ISR(WDT_vect) {} // Nichts tun, wird aber für das Aufwachen benötigt
void enableWatchdog() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
wdt_reset();
WDTCSR |= bit(WDCE) | bit(WDE); // Setze WDCE um WDE setzen/ändern zu können
// WDTCSR = bit(WDIE) | bit(WDP3); // 4s / interrupt, kein System Reset
WDTCSR = bit(WDIE) | bit(WDP3) | bit(WDP0); // 8s / interrupt, kein System Reset
}
}
// Lege den Microcontroller schlafen
void enterSleep(void) {
wdt_reset();
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // EDIT: SLEEP_MODE_PWR_DOWN - niedrigster Stromverbrauch.
sleep_enable();
sleep_mode(); // Starte Schlafmodus
// Nach dem Aufwachen, wird das Programm genau hier weiterlaufen
sleep_disable(); // Erste Aktion muss das Auschalten des Schlafmodus sein.
}
void setup() {
Serial.begin(115200);
Serial.println("Start...");
power_adc_disable();
enableWatchdog();
}
void loop() {
Serial.println("LEDs anschalten");
delay(2000);
Serial.println("LEDs ausschalten... Gute N8");
delay(20);
// In den Schlafmodus wechseln
// 32 / 8 = 4 mal aufwachen und gleich wieder schlafen -> 32 Sekunden Schlafzeit
for (size_t i = 0; i < (SLEEP_ZEIT / WATCHDOG_TIME); ++i) {
enterSleep();
}
}