Probleme mit WDT beim ATTiny85 als Schalter für ESP8266 + Interrupt Button für Wetterdisplay

Gast #5787710
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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren