Arduino Sleep

Hallo, ich mache meine ersten Gehversuche mit Arduino Nano und einem Projekt dazu.
Durch viel lesen im Internet gehts auch gut voran, nun stolper ich aber über die Sleep Funktion.

Mein Anliegen, ich frage an einem Analog Pin einen Zustand ab, ist der Pin >999 sag ich "mache deine Arbeit", ist < 999 "geh schlafen".

soweit sogut, im Internet ein feines Beispiel dazu gefunden, geht auch soweit.
Mein Problem das Aufwachen, bzw. versteh ich nicht was er wirklich in der Schlafphase tut.

hier erstmal die wichtigen Auszüge aus dem Sketch.

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

volatile int sleepcounter = 0; // Schlafzyklen mitzählen

int campin = A3;
int lanpin = A4;
int routerpin = A5;
int solarpin = A7;

//bla bla bla
void setup() {  
  pinMode (lanpin, OUTPUT);         //LAN Pin
  pinMode(campin, OUTPUT);          //Cam Pin
  pinMode(routerpin, OUTPUT);       //Router Pin
  pinMode(solarpin, INPUT);         //Solar Pin

watchdogOn(); // Watchdog timer einschalten für Sleep

  currentTime = millis();        //Zeitzeug
}

void loop()
{
//Laufzeit des Boards   
  currentTime = millis();
//Zeitfunktion aufrufen
  uptime(); 
//Werte  setzem
   if (secs==00) { 
   //schauen ob Solar anliegt
   int solarvalue = analogRead(solarpin);
   if (solarvalue <= 999) {            
   digitalWrite(lanpin, LOW);           //LAN anschalten LOW=AUS, HIGH=AN
   digitalWrite(campin, LOW);           //Cam LOW=AUS, HIGH=AN
   digitalWrite(routerpin, LOW);        //Router LOW=AUS , HIGH=AN
   pwrDown(180); //180Sekunden Sleep da kein Solar
  } 
}
delay(1000);
}

void uptime()
{
 secs = currentTime/1000; //convect milliseconds to seconds
 mins=secs/60; //convert seconds to minutes
 hours=mins/60; //convert minutes to hours
 secs=secs-(mins*60); //subtract the coverted seconds to minutes in order to display 59 secs max
 mins=mins-(hours*60); //subtract the coverted minutes to hours in order to display 59 minutes max
}

void pwrDown(int sekunden) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // den tiefsten Schlaf auswählen PWR_DOWN
  for(int i=0; i < sekunden; i++) {
    sleep_enable(); // sleep mode einschalten
 power_all_disable ();// turn off various modules
 // power_adc_disable(); // ADC converter
 power_spi_disable(); // SPI
 power_usart0_disable();// Serial (USART)
 // power_timer0_disable();// Timer 0
 // power_timer1_disable();// Timer 1
 // power_timer2_disable();// Timer 2
 power_twi_disable(); // TWI (I2C)
    sleep_mode(); // in den sleep mode gehen
   
   sleep_disable(); // sleep mode ausschalten nach dem Erwachen
   power_all_enable();
   int solarvalue = analogRead(solarpin);
   if (solarvalue <= 999) {
   digitalWrite(lanpin, LOW);        //LAN anschalten LOW=AUS, HIGH=AN
   digitalWrite(campin, LOW);        //Cam aus=LOW AN=HIGH
   digitalWrite(routerpin, LOW);     //Router LOW=AUS , HIGH=AN
    pwrDown(180); //180Sekunden Sleep da immer noch kein Solar
  } else {
  asm volatile ("jmp 0"); 
  }
 }
}
void watchdogOn() {
  MCUSR = MCUSR & B11110111; // Reset flag ausschalten, WDRF bit3 vom MCUSR.
  WDTCSR = WDTCSR | B00011000; // Bit 3+4 um danach den Prescaler setzen zu können
  WDTCSR = B00000110; // Watchdog Prescaler auf 128k setzen > ergibt ca. 1 Sekunde
  WDTCSR = WDTCSR | B01000000; // Watchdog Interrupt einschalten
  MCUSR = MCUSR & B11110111;
}

ISR(WDT_vect) {
  sleepcounter ++; // Schlafzyklen mitzählen
}

so wer sich jetzt die Mühe gemacht hat da durch zu steigen ...........................

zu Deutsch, im Loop frage ich jede Minute ab Solar JA/NEIN wenn NEIN (<999) dann geh schlafen.

So nun wenn er wieder Aufwacht, will ich eigentlich das er schaut ob Solar anliegt, wenn NEIN wieder schlafen, wenn JA einfach komplett neu starten, weil ich find das am einfachsten das sich alles andere neu initialisiert.
Bei mir ist es im Moment so, er geht schlafen, wenn ich nun Solar JA sage (per anlegen 5V) fährt er SOFORT neu hoch, wie kann das sein? er müsste doch noch im Schlafmodus sein wenn ich 5V anlege und erst wenn die 180Sekunden um sind schauen ob PIN > 999 ???
Ich glaube der schläft nicht richtig?

Danke Heiko

Je nach Schlafmodus laufen die Timer etc. mit, so daß der Controller regelmäßig wieder aufwacht, oder er kann nur noch auf einen externen Interrupt reagieren. Die Auswahl des passenden Schlafmodus ist einfacher (und empfehlenswerter) als das einzelne Abschalten unterwünschter Interrupts.

Wenn Du den Controller alle 180 Sekunden weiterlaufen lassen möchtest, dann müssen dafür die Timer (zumindest T0) weiterlaufen, und regelmäßig Interrupts liefern. Dann fragt man in loop() ab, ob die Weckzeit schon erreicht ist, und legt den Controller andernfalls gleich wieder schlafen. So wie Du das in pwrDown() vorhast, funktioniert das nicht.

Wenn der Controller schon mal wach ist, kann man in loop() auch das Aufwach-Signal (mit analogRead) abfragen, und entsprechend weitermachen oder wieder schlafen gehen.

OK Danke.

Gibts irgendwo ein Stück Code dafür ?

Ich versteh nicht so recht wo es "nach" dem Aufwachen weiter geht im Code, daher wo soll ich erneut abfragen ob sich der Zustand des Pin geändert hat?

Danke Heiko

Es geht weiter direkt nach

   sleep_mode(); // in den sleep mode gehen

Danach läuft loop() wieder, und dort kann man abfragen was man braucht.

OK, hab aus dem Link ein Bsp. genommen welches funktioniert, das mit dem Wakeup at Interupt (Sketch J).

Wenn ich den nun noch von meinen Pin A7 wecken könnte wäre es fertig.

Das andere versuch ich auch mal.

Danke

A6 und A7 sind rein analog. Wie soll da ein Interrupt gehen?

Was evtl. gehen würde ist ein Pin Change Interrupt auf einem der anderen Analog Pins. Dann könnte man aufwachen wenn der Pegel über die High-Schwelle ansteigt und danach messen.

Pin Change Interrupts sind aber recht kompliziert. Man könnte auch das Signal gleichzeitig auf einen der normalen externen Interrupt Pins und einen Analog Pin geben.

Oder wenn du eh nur bei einer bestimmten Schwelle aufwachen willst, bau doch einfach einen Komparator vor einen Interrupt-Eingang. Der interne Komparator des Arduino (ein oft verschwiegenes Feature das nicht durch die IDE unterstützt wird) kann auch direkt einen Interrupt auslösen. Ich weiß aber nicht ob man damit aus dem Sleep Modus aufwachen kann. Sollte eigentlich gehen.
Hier gibt es eine Library dafür:
http://www.leonardomiliani.com/en/2012/analogcomp-una-libreria-per-gestire-il-comparatore-analogico/
Vergleicht die Spannung an zwei Pins und macht was wenn eine höher als die andere ist (je nach Anschluss und Konfiguration). Dann schließt man das Signal an einen Pin an und an den anderen einen Spannungsteiler oder einen Trimmer um die Vergleichssspannung einzustellen.

mal blöd gefragt,
einfach von A7 mit ner Diode zu D2? D2 im Code auf LOW also das er auf HIGH reagiert.
Wenn A7 5V anliegt = D2 HIGH ?

und......der Code einzeln aus dem Link = 20mA im Sleep, eingefügt in meinen Code = 80mA im Sleep.
Irgend etwas geht in meinem Code nicht aus, obwohl ich alle PINs INPUT habe wenn er schlafen soll? Den Rest einfach übernommen aus dem Link. Einzeln geht ja auch.Hab nur noch die Ethernet.h per include drinnen, muss ich die "entladen", oder Timer noch einzeln stoppen?

Danke Heiko

Die Pins sind normal als Eingang definiert. Da muss man sonst nichts machen, weil nichts passieren kann

Aber wie gesagt, wäre das sauberste ein Komparator.

.der Code einzeln aus dem Link = 20mA im Sleep, eingefügt in meinen Code = 80mA im Sleep.

Spannungsregler, USB Konverter und LEDs brauchen einiges an Strom. Was auf der Seite steht gilt für einen nackten Prozessor wenn sonst nichts dabei steht. Wobei 80mA etwas hoch ist. Ein UNO im Schlafmodus braucht etwa 35mA

Ich weiß nicht, was an den Pin Change Interrupts kompliziert sein soll, außer daß es dafür keine Standard-Funktionen gibt.

Jeder Port hat einen Interrupt Vektor (und damit ISR) und Interrupt Enable Bit, jeder Pin nochmal ein eigenes Enable Bit.

Zudem sind diese Interrupts asynchron, funktionieren also in jedem Schlafmodus.

Achja, ich komm nicht ans Ziel :confused:

Also das Board tut so als würde es schlafen, lässt sich aufwecken alles fein, NUR, irgendwie schläft es nicht wirklich.
Wenn ich den Schlaf Code einzeln einspiele schläft es bei 20mA das ist fein, wenn ich meinen kompletten Code einspiele schläft es bei 80mA, also irgend welche PINs oder Funktionen laufen weiter, ich vermute es liegt an der Ethernet Lib, ich denke die "krallt" sich irgendwelche PINs und die kann ich im Sleep nicht ausschalten.
Ich hab jetzt versucht eine 0 bzw eine 1 ins EEprom zu schreiben, bei 0 arbeite, bei 1 schlafe, ich starte den Chip neu per

asm volatile (" jmp 0");

selbst da 80mA weil da ja auch nicht alles "entladen" wird.

Man man man..............weiß nicht weiter.............

Versucht alle PINs zu INPUT ohne Erfolg
alle Timmer aus ohne Erfolg

wo soll es noch hängen? Der Schlaf Code einzeln geht ja und ich mach ja nix weiter als an 3 Ausgängen was anschalten und an einem Eingang was empfangen. Kann ja nur das Ethernet sein?

Grüße Heiko

asm volatile ("  jmp 0");

Gerade wenn man sowas macht, ist das wahrscheinlich keine gute Idee. Das springt lediglich an den Anfang des Codes ohne die Register zurückzusetzen.

Einen richtigen Software-Reset macht man über den Watchdog Timer:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_softreset
WTD starten und dann in eine Endlos-Schleife gehen, so dass der Timer nicht zurückgesetzt wird und gleich einen Reset auslöst

Es ist durchaus denkbar, daß das Ethernet Modul regelmäßig für Interrupts sorgt, müßte man mal in der Bibliothek nachschauen. Vielleicht läßt sich dort einstellen, wie oft die Verbindung geprüft wird.

Laß Dir doch mal ausgeben, wie oft (pro Minute...) der Controller aufgeweckt wird, obwohl er weiterschlafen sollte. Dann kannst Du feststellen, wie sich diese Zahl ändert, wenn Du die verschiedenen Bibliotheken einbindest.

Wieviel Strom braucht denn das Ethernet Modul? Macht es überhaupt Sinn, mit allem anderen Arduino-Overhead neben dem atmega328p, diesen überhaupt schlafen zu legen?

Ich dachte, sleep ist gut, um von 15mA auf 0.3mA zu kommen :wink:

OK , warum Sleep, das Teil wird Solar betrieben, ich bekomme eine Info vom Solarregler Solar JA = 5V zum auswerten, Solar NEIN = 0V zum Auswerten.
Soll bewirken das das Teil Nachts "aus" ist und bei schlechtem Wetter auch, UND selbständig wieder an geht wenn die Sonne wieder da ist.
Das Prinzip funktioniert ohne Arduino, Eigenbau mit Bascom sehr gut.
Arduino aus dem Grunde, die Ethernet Sache ist viel einfacher als in Bascom und dann gibts noch eine Funktion die in Arduino funktioniert, aber in Bascom ich nicht hin bekomme, daher der Umstieg.

Ich will doch nur folgendes, kann doch nicht so schwer sein

void loop()
{
mache dein Zeug  // brauch nicht ersetzt werden

int solarvalue = digitalRead(solarpin);//schau ob Solar anliegt
if (solarvalue == 0) { 
sleep(); 
  } 
}
void sleep()
{

schalte alle Pins auf LOW, also alles aus
mache alles was du nicht brauchst aus = Stromsparen

set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
    sleep_enable();
    noInterrupts ();
    // will be called when pin D2 goes low  
    attachInterrupt (0, wake, CHANGE);
    EIFR = bit (INTF0);  // clear flag for interrupt 0
    // interrupts are turned on.
    interrupts ();  // one cycle
    sleep_cpu ();   // one cycle
}
void wake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (0);

starte einfach neu als ob ich den Strom weggenommen habe

  
}  // end of wake

so wer kann das deutsche im Code ersetzen in Arduino Sprache?

Mir gehts nicht um 10 oder 20mA, mir gehts darum das die 3 Geräte die vom Arduino an geschalten werden, dann Nachts ausgeschaltet werden, UND wenn wieder Tag ist sich das Ethernet Modul neu initialisiert, was es nicht tut bei mir, daher einfach "starte neu als wenn Strom weg war".
In Bascom frag ich ganz am Anfang den Solarpin ab, ist LOW->warte 30min dann Neustart. Mehr nicht, das geht ja in Arduino nicht weil ich über void setup kein if schreiben kann und weil der ja eh nicht richtig neu startet als wäre Strom weg.
Wenn das so einfach ginge könnt ich mir den ganzen Sleep Kram sparen.

das war das letzte Projekt mit Arduino , ich bleib bei meinem Bascom da gibts so schnickschnak nicht.

Serenifly:
A6 und A7 sind rein analog. Wie soll da ein Interrupt gehen?

Nimmt man stattdessen AIN0 und AIN1, kann der Analog Comparator den Interrupt auslösen, ohne daß regelmäßig im Code nachgeschaut wird.

So, es funktioniert ! :slight_smile:

Eure Hinweise waren alle Super, nur hat es in der Praxis nicht funktioniert, nicht weil ich zu blöd bin, nein die Hardware war Schuld.
Problem war ja das ich per Watchdog keinen Neustart hin bekommen hab, alles andere ging ja nach und nach. Tjo zufällig bin ich auf eine Seite gekommen, wo genau dieses Problem behandelt wurde.
Es war ein Fehlerhafter Bootloader installiert........... :confused:

Also neuen Bootloader installiert und schon passt alles, muss man mal drauf kommen........

Na mal sehen was mich nun noch alles erwartet, jetzt gehts weiter.....

Dank euch Heiko

Und wenn du jetzt noch sagst wie deine Lösung genau aussieht, dann haben andere mit dem gleichen Problem auch was davon :slight_smile:

(also z.B. Link zur Seite mit der Lösung etc.)

Danke!

OK kein Problem,

da steht das mit dem Bootloader......

eine Kleinigkeit hab ich schon wieder :o

Wenn ich jetzt im Sleep Modus wirklich alles deaktivieren will, gehts nicht.

DDRB = B00000000;       // set Arduino pins B Input, 0=Input, 1=Output, alle PortB brauch ich nicht
PORTB = B00000000;      // alle Pins B to Low, 0=Low, 1=High
DDRC = B00000000;       // set Arduino pins C Input, 0=Input, 1=Output, 3,4,5 als Out lassen
PORTC = B00000000;      // alle Pins C to Low, 0=Low, 1=High
DDRD = B00000000;       // set Arduino pins D Input, 0=Input, 1=Output
PORTD = B00000000;      // alle Pins D to Low, 0=Low, 1=High

das geht nicht, da wacht er immer von allein wieder auf, komisch auch das z.B. PIN D2 mit einem 10K Pulldown versehen ist und im Programm auf HIGH sauber reagiert, in der Sleep Prozedur siehe Code knallt der PIN plötzlich von 0 auf 5V dann wieder auf 0V ????

Noch komischer, wenn ich nur

DDRC = B00000000;       // set Arduino pins C Input, 0=Input, 1=Output, 3,4,5 als Out lassen
PORTC = B00000000;      // alle Pins C to Low, 0=Low, 1=High
DDRD = B00000000;       // set Arduino pins D Input, 0=Input, 1=Output
PORTD = B00000000;      // alle Pins D to Low, 0=Low, 1=High

Port C und D setze, also Port B weg lasse funktioniert alles, nur das der Nano dann mit 80mA schläft, statt mit 20mA
Was hat Port B bitte nun mit Port D zu tun?

Edit: ich glaub ich habs gefunden, an B6 und B7 hängt ja der Quarz, da wird das so nicht gehen....

Außerdem erhöhen "schwebende" Pins den Stromverbrauch.
Es ist also kontraproduktiv alle Pins auf INPUT zu stellen, und dann auch noch die Pullups alle aus zu machen.