Forum: Mikrocontroller und Digitale Elektronik Probleme mit WDT beim ATTiny85 als Schalter für ESP8266 + Interrupt Button für Wetterdisplay


von Chris (Gast)


Lesenswert?

Hallo Ihr!

Vor ca. 2 Monaten habe ich mir meinen ersten µC einen ATTiny85 gekauft. 
Eigentlich nur aus der Not heraus.
Ich habe mir ein e-Paper Display gekauft welches ich via ESP8266 über 
die SPI Schnittstelle mit Daten versorge. Die Wetterdaten bzw. 
Temp-Daten der Haussensoren bezieht der ESP8266 von meinem RaspberryPi. 
Das funktioniert soweit wunderbar.
Nun zu den Aufgaben des Attiny:
Das Display soll alle 5 Min aktualisiert werden indem der WDT auslöst 
und der ESP mit Spannung versorgt wird. Nachts soll übrigens nur nur 
alle 30 Min aktualisiert werden.
Durch den Druck auf einen Taster, sollen andere Daten auf dem epaper 
Display via ESP angezeigt werden. Dazu soll der ATTiny via Interrupt aus 
dem WDT DeepSleep geholt werden, der ESP mit Spannung versorgt werden un 
ca. 300ms später einen Eingang am ESP HIGH ziehen.
Nach einer kurzen Zeit kehrt das Display wieder zur Hauptansicht zurück.

Hintergründe:
- Das Zeitversetzte HIGH setzen eines Eingangs, hat den Hintergrund, 
dass ein sofortiges anlegen dazu führt, dass der ESP nicht startet weil 
dieser Pin beim einschalten LOW sein muss. (Nein, ich kann nicht auf 
einen anderen Pin wechseln.)

- Der unterschied zwischen Tag und Nachtschaltzeit realisiere ich 
dadurch, dass ein anderer Pin am ESP ein PWM Signal in einer bestimmten 
"Höhe" gibt. >900 für Nachts, <900 && >400 für Tagsüber, >300 && <400 
OTA Update. Dieser wird über ein 22µF Kondensator + 10k Widerstand am 
Attiny ausgelesen.

- Ich schalte am ATTiny über einen pnp Transistor die 3,3v für den ESP.

- Zur Überwachung/Debugging gibt es noch eine LED.

Problem:

- Es kommt immer mal wieder zu Zeitweisen ausfällen bzw. dass sich der 
Attiny aufhängt und garnichts mehr macht.
- Öfter scheint es so, als würde der WDT mehrmals auslösen trotzdem er 
via wdt_disable() an verschiedensten stellen schon disable gesetzt 
wurde.
- Es ist im allgemeinen kaum ein konsistentes Verhalten zu sehen. Wird 
der Button gedrückt setzt er den ESP unter Spannung für 2 Sekunden ca. 
schaltet ihn dann wieder ab um ihn kurz drauf wieder einzuschalten.
Ein verhalten welches ich mir nicht erklären kann.

Wie evtl. schon im Text erwähnt. Ich sitze nun seit über einem Monat 
daran irgendwie das Ding zum laufen zu bringen und bin inzw. wirklich 
verzweifelt. Dementsprechend sieht inzw. auch der Code(den ich schon zum 
3. mal neu programmiert habe) etwas Rummelig aus.

Dennoch, vielleicht könnt ihr mir helfen. Vielleicht übersehe ich 
irgendwo etwas. Vielleicht verwende ich den Watchdog falsch. Ich weiß es 
nicht bin völlig verzweifelt!
Was ich nicht ganz verstehe, ist, wieso muss ich in meiner Sleep 
Funktion noch ein If mit Break einbauen, um via Taster Interrupt aus dem 
Sleep aufzuwachen? Habe schon probiert den WDT auf disable zu setzen 
aber das führt zu überhaupt nichts. Ist das so, wie ich es gelöst habe 
überhaupt korrekt?
1
#include <avr/sleep.h>
2
#include <avr/interrupt.h>
3
#include <avr/wdt.h>
4
#include <PinChangeInterrupt.h>
5
6
7
// ATMEL ATTINY 25/45/85 / ARDUINO
8
//
9
//                  +-\/-+
10
// Ain0 (D 5) PB5  1|    |8  Vcc
11
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
12
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
13
//            GND  4|    |5  PB0 (D 0) pwm0
14
//                  +----+
15
#define sleepInput A1
16
#define Transistor 1
17
#define LED 4
18
#define Taster 3
19
#define TSignal 0
20
//uint16_t nbr_remaining;
21
byte adcsra_save;
22
volatile uint16_t siVal = 0, resetCNT = 0, nbr_remaining = 0;
23
volatile bool intrInp = false, intrBTN = false;
24
uint16_t  timeout = 22 * 1000; //sekunden
25
unsigned long millisalt = 0;
26
uint16_t  TagTime = 5 * 60 / 8;
27
uint16_t  NachtTime = 30 * 60 / 8;
28
uint16_t  SecondScreenTime = 0.4 * 60 / 8;
29
30
31
void setup() {
32
  wdt_disable();
33
 
34
  analogReference(DEFAULT);
35
  pinMode(sleepInput, INPUT);
36
  pinMode(Taster, INPUT_PULLUP);
37
  pinMode(TSignal, OUTPUT);
38
  pinMode(LED, OUTPUT);
39
  pinMode(Transistor, OUTPUT);
40
41
  digitalWrite(LED, LOW);
42
  digitalWrite(TSignal, LOW);
43
  digitalWrite(Transistor, HIGH);
44
45
46
  attachPinChangeInterrupt(Taster, TasterHandler, FALLING);
47
48
  delay(1000);
49
  digitalWrite(LED, HIGH);
50
  delay(2000);
51
  digitalWrite(LED, LOW);
52
53
54
  sleep(1);
55
}
56
57
void loop() {
58
59
  digitalWrite(LED, HIGH);
60
  delay(15);
61
  digitalWrite(LED, LOW);
62
  delay(15);
63
64
}
65
66
ISR(WDT_vect)
67
{
68
  
69
70
  //wdtDisable();  // Nur zur Testzwecken
71
 
72
73
74
}
75
76
77
void TasterHandler() {
78
79
80
  intrBTN = true;
81
82
83
}
84
85
86
void TasterWake() {
87
  // wdt_disable();
88
  uint16_t sleepTime = 0, siValMax = 0;
89
  millisalt = millis();
90
  bool Stop = false;
91
  disablePCINT(digitalPinToPCINT(Taster));
92
  digitalWrite(TSignal, LOW);   // Taster Signal. Wird High gesetzt nach 300ms damit der ESP weiß, dass er den 2. Bildschrim zeigen soll.
93
  digitalWrite(Transistor, LOW);
94
  digitalWrite(LED, HIGH);
95
  delay(300);
96
  digitalWrite(TSignal, HIGH);
97
  //digitalWrite(LED, LOW);
98
99
100
// Auf Antwort vom ESP Warten durch auslesen von "sleepInput". Dadurch weiß der Attiny das er sich nun ausschalten kann. Fällt SleepInput wieder herab hat er das maximum erreicht und beendet den loop. Falls dies nicht funktioniert wird via Millis ein Timeout von 22 Sek. berücksichtigt.
101
  do { 
102
    siVal = analogRead(sleepInput);
103
    if (siVal > siValMax) {
104
      siValMax = siVal;
105
106
      delay(5);
107
      // wdt_reset();
108
    }
109
    else if ((siValMax > 100) && ((siVal + 50) < siValMax)) {
110
      Stop = true;
111
      break;
112
113
    }
114
    else if (millis() - millisalt >= timeout ) {
115
116
      digitalWrite(LED, HIGH);
117
      delay(5000);
118
      digitalWrite(LED, LOW);
119
      // wdt_reset();
120
      sleepTime = TagTime;
121
      //intrInp = false;
122
      Stop = true;
123
      break;
124
    }
125
126
127
  } while (Stop == false);
128
129
130
// Auswerten der Ergebnisse:
131
  if (siVal > 400) {
132
    for (byte i = 0; i < 2; i++) {
133
      digitalWrite(LED, HIGH); //FEHLER
134
      delay(100);
135
      digitalWrite(LED, LOW); //FEHLER
136
      delay(200);
137
      // wdt_reset();
138
    }
139
140
    sleepTime = SecondScreenTime;
141
142
143
  } else if ((siValMax < 400) && (siValMax > 280)) {
144
145
146
    for (byte i = 0; i < 30; i++) {
147
      digitalWrite(LED, HIGH);
148
      delay(5000);
149
      // wdt_reset();
150
      digitalWrite(LED, LOW);
151
      delay(1000);
152
      // wdt_reset();
153
    }
154
    digitalWrite(LED, LOW);
155
    sleepTime = 0;
156
157
  } else if (millis() - millisalt >= timeout ) {
158
    sleepTime = SecondScreenTime;
159
160
    // wdt_reset();
161
    digitalWrite(LED, LOW);
162
    delay(500);
163
    digitalWrite(LED, HIGH);
164
    delay(6000);
165
    digitalWrite(LED, LOW);
166
    // wdt_reset();
167
168
169
  }
170
171
172
  delay(250);
173
  enablePCINT(digitalPinToPCINT(Taster));
174
  digitalWrite(TSignal, LOW);
175
  digitalWrite(Transistor, HIGH);
176
  digitalWrite(LED, LOW);
177
  // wdt_reset();
178
  intrBTN = false;
179
  intrInp = false;
180
  sleep(sleepTime);
181
}
182
183
184
// Funktion die aufgerufen werden soll, wenn der Attiny durch alle Sleepcycles durch ist. Funktioniert nach dem gleichen Prinzip wie die Tasterfunktion weiter oben.
185
186
void WDTwake(void) {
187
  // wdt_disable();
188
  // wdt_reset();
189
  millisalt = millis();
190
  bool Stop = false;
191
  uint16_t sleepTime = 0, siValMax = 0;
192
193
  //detachPCINT(digitalPinToPCINT(Taster));
194
  disablePCINT(digitalPinToPCINT(Taster));
195
  digitalWrite(Transistor, LOW);
196
197
  delay(50);
198
199
  do {
200
201
202
    siVal = analogRead(sleepInput);
203
    if (siVal > siValMax) {
204
      siValMax = siVal;
205
206
      delay(5);
207
      // wdt_reset();
208
    }
209
    else if ((siValMax > 100) && ((siVal + 50) < siValMax)) {
210
      Stop = true;
211
      break;
212
213
    }
214
    else if (millis() - millisalt >= timeout ) {
215
      digitalWrite(LED, HIGH);
216
      delay(5000);
217
      // wdt_reset();
218
      digitalWrite(LED, LOW);
219
      sleepTime = TagTime;
220
      //intrInp = false;
221
      Stop = true;
222
      break;
223
    }
224
225
226
  } while (Stop == false);
227
228
229
230
231
  if ((siValMax < 850) && (siValMax > 400)) {
232
    for (byte i = 0; i < 2; i++) {
233
234
      digitalWrite(LED, HIGH); //FEHLER
235
      delay(1000);
236
      digitalWrite(LED, LOW); //FEHLER
237
      delay(200);
238
      // wdt_reset();
239
    }
240
    sleepTime = TagTime; //FIXME
241
    //intrInp = false;
242
    //    break;
243
  }
244
  else if (siValMax > 900) {
245
246
    for (byte i = 0; i < 4; i++) {
247
248
      digitalWrite(LED, HIGH); //FEHLER
249
      delay(1000);
250
      digitalWrite(LED, LOW); //FEHLER
251
      delay(200);
252
      // wdt_reset();
253
    }
254
255
    // wdt_reset();
256
    sleepTime = NachtTime; //FIXME
257
    //intrInp = false;
258
    //  break;
259
  }
260
  else if ((siValMax < 400) && (siValMax > 280)) {
261
    for (byte i = 0; i < 30; i++) {
262
      digitalWrite(LED, HIGH);
263
      delay(5000);
264
      // wdt_reset();
265
      digitalWrite(LED, LOW);
266
      delay(1000);
267
      // wdt_reset();
268
    }
269
    sleepTime = 0;
270
271
  }
272
  else if (millis() - millisalt >= timeout ) {
273
    // wdt_reset();
274
    delay(500);
275
    digitalWrite(LED, HIGH);
276
    delay(5000);
277
    digitalWrite(LED, LOW);
278
    sleepTime = TagTime;
279
    // wdt_reset();
280
281
  }
282
283
284
  delay(250);
285
  //attachPCINT(digitalPinToPCINT(Taster), TasterHandler, CHANGE);
286
  enablePCINT(digitalPinToPCINT(Taster));
287
  digitalWrite(Transistor, HIGH);
288
  digitalWrite(LED, LOW);
289
  // wdt_reset();
290
  intrBTN = false;
291
  intrInp = false;
292
  sleep(sleepTime);
293
}
294
295
296
297
298
299
300
301
// Aktuell nicht mehr im Einsatz. Gedacht als Absicherung, falls er mal wieder hängt. Funktionierte nicht. KA warum.
302
void ResetMCU(void)
303
{
304
  for (byte i = 0; i < 10; i++) {
305
    digitalWrite(LED, HIGH);
306
    delay(50);
307
    digitalWrite(LED, LOW);
308
    delay(150);
309
  }
310
  /*
311
    cli();
312
    wdt_reset();                  // reset watchdog timer
313
    MCUSR &= ~(1 << WDRF);        // clear reset flag
314
    WDTCR = (1 << WDE) | (1 << WDCE); // enable watchdog
315
    WDTCR = (0 << WDIE) | (1 << WDE) | (1 << WDP1) ; // watchdog interrupt instead of reset
316
    //+reset, timeout can be 15,30,60,120,250,500ms or 1,2,4,8s
317
    sei();
318
  */
319
320
}
321
322
//Händisch programmierte WDT Disable Funktion, falls wdt_disable() evtl. nicht korrekt funktioniert. Allerdings macht es keinen Unterschied.
323
void wdtDisable() {
324
325
  cli();
326
  wdt_reset();                  // reset watchdog timer
327
  MCUSR &= ~(1 << WDRF);        // clear reset flag
328
  WDTCR = (1 << WDE) | (1 << WDCE); // enable watchdog
329
  WDTCR = (0 << WDIE) | (0 << WDE) ;// watchdog interrupt instead of reset
330
331
  sei();
332
  delay(2);
333
334
}
335
336
// Erstellen des Watchdogs
337
void config_wdt(void)
338
{
339
  cli();
340
  wdt_reset();                  
341
  MCUSR &= ~(1 << WDRF);       
342
  WDTCR = (1 << WDE) | (1 << WDCE); 
343
  WDTCR = (1 << WDIE) | (0 << WDE) | (1 << WDP0) | (1 << WDP3);// | (1 << WDP1); // watchdog auf interrupt stellen
344
  //+reset, timeout can be 15,30,60,120,250,500ms or 1,2,4,8s
345
  sei();
346
347
348
349
}
350
351
352
353
// ATTINY Schlafen legen für (Anzahl Zyklen). Ist dies vorbei, müsste eigentlich ja der Code direkt nach dem befehl sleep_mode() aufgerufen werden?!
354
void sleep(int ncycles) {
355
  config_wdt();
356
  //adcsra_save = ADCSRA;
357
  if (ncycles <= 0) {
358
    nbr_remaining = 1;
359
  } else {
360
    nbr_remaining = ncycles;
361
  }
362
363
364
  cli();
365
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
366
  sleep_enable ();
367
  //ADCSRA = 0; //vermutlich nicht nötig wegen powerall disable
368
  sleep_bod_disable();
369
  //power_all_disable ();
370
  //noInterrupts ();
371
  ADCSRA &= ~_BV(ADEN);
372
  sei();
373
374
375
376
  do { // while some cycles left, sleep!
377
378
379
    //config_wdt();
380
381
    wdt_reset();
382
383
    sleep_mode();
384
385
386
387
    //wdt_disable();
388
    if (intrBTN == true) {
389
390
      break;
391
    }
392
    nbr_remaining = nbr_remaining - 1;
393
  } while (nbr_remaining > 0) ;
394
395
396
397
  cli();
398
  wdtDisable();
399
  ADCSRA |= _BV(ADEN);
400
  //ADCSRA = adcsra_save; //läuft eigentlich
401
  sei();
402
403
404
  if ((intrBTN == true)) {
405
406
    for (byte i = 0; i < 4; i++) {
407
      digitalWrite(LED, HIGH);
408
      delay(100);
409
      digitalWrite(LED, LOW);
410
      delay(100);
411
    }
412
413
    TasterWake();
414
415
  }
416
  else if ((nbr_remaining <= 0) && (intrBTN == false) ) {
417
418
    for (byte i = 0; i < 20; i++) {
419
      digitalWrite(LED, HIGH);
420
      delay(100);
421
      digitalWrite(LED, LOW);
422
      delay(100);
423
    }
424
425
426
    WDTwake();
427
428
  }
429
430
// Test "else" um zu schauen ob es zu Fehlern kommt. 
431
  else {
432
    for (byte i = 0; i < 50; i++) {
433
      digitalWrite(LED, HIGH);
434
      delay(10);
435
      digitalWrite(LED, LOW);
436
      delay(20);
437
    }
438
    sleep(1);
439
440
  }
441
442
  // wdt_reset();
443
444
445
446
}


Schon einmal vielen Dank für eure Hilfe!

: Verschoben durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.