home
Flattr this
erste Version am 05.10.2015
letzte Änderung am 21.11.2015

Rollotron Schwenkwickler ansteuern


Bei diesem Projekt werde ich mal von Anfang an meine Überlegungen in HTML erfassen und Zwischenschritte sowie ggf. auch Irrwege dokumentieren.

Im ersten Schritt werden die Signale einer IR-Fernbedienung für Rollladen-Motoren eingelesen bzw. gemessen.
Danach wird dann eine Schaltung und ein Programm entwickelt, um diese IR-Signale programmgesteuert ausgeben zu können - mit dem Ziel, fünf Rollläden zu definierten (und über das Jahr wechselnden) Zeiten rauf- bzw. runterzufahren.


Inhalt
  1. Rollotron Schwenkwickler ansteuern
  2. Motivation
  3. Schritt 1 - Einlesen der IR-Fernbedienung
  4. Schritt 2 - Ausgeben der IR-Signale
    1. Analyse bzgl. Verbesserungsmöglichkeiten
    2. Erkenntnisse ins Programm einbauen
  5. Schritt 3 - Zeitsteuerung zufügen
  6. Schritt 4 - weitere Überlegungen...
    1. ...zum Stromverbrauch
    2. ...zum Stellen der Uhr
  7. Schritt 5 - ESP8266 (vor der Lieferung)
  8. Überlegungen zur Kommunikation mit der Rollladensteuerung
  9. Schritt 6 - die ESP8266-Module sind da
  10. Grundeinrichtung des ESP8266
  11. das Programm ist quasi fertig
  12. Rückschläge und Erweiterungs-Ideen
  13. die Pegelwandler sind da
  14. 3.3V Festspannungsregler
  15. Schritt 7 - die finale Schaltung
  16. der erste Arbeitstag
  17. Schritt 8 - letzter Schliff vor dem Einsatz
  18. Schritt 9 - Gehäuse-Bau und Dauerbetrieb
  19. Nachlese
  20. Ideen für eine mögliche nächste Version


Motivation

Wir haben im Wohnzimmer fünf Rollläden, die alle mit jeweils einem "Rademacher Rollo Tron Schwenkwickler Comfort 9540" rauf- bzw. runtergefahren werden.
Diese Schwenkwickler können ihre Aufgabe zeitgesteuert erledigen oder manuell am Gerät als auch mit einer IR-Fernbedienung bedient werden.
Seit der Anschaffung vor gut zwei Jahren laufen die Dinger hier primär zeitgesteuert. Das klappt auch ganz passabel (und die Uhren laufen sogar nach einem ca. viertelstündigen Stromausfall korrekt weiter).
Trotzdem stören mich zwei Dinge:
  1. die Uhren lassen sich nicht auf die Sekunde genau synchronisieren. Somit fahren die Rollläden bestenfalls alle in der selben Minute rauf oder runter. Aber nie so richtig schön "exakt zeitgleich". Vier der fünf Rollläden sind direkt nebeneinander angebracht...da sähe es viel hübscher aus, wenn sie sich alle zu jedem Zeitpunkt auf der selben Höhe befinden würden.
  2. über das Jahr muss ich die Zeiten zum Runterfahren mehrmals der Jahreszeit anpassen. Im Sommer erst um 23:00 Uhr, im tiefsten Winter aber schon um 18:00 Uhr - mit mehreren Zwischenschritten bzw. -zeiten über die Monate. Das nervt total.

Außerdem habe ich seinerzeit die fernbedienbaren Versionen (die damals pro Stück etwa 30€ teurer als die nicht fernbedienbaren Modelle waren) samt IR-Fernbedienung extra deswegen gekauft, um genau dieses Projekt irgendwann mal realisieren zu können. Und nun habe ich gerade mal wieder Lust auf ein Hardware-Projekt :-)

Ziel soll sein, dass ein Arduino (oder final eher ein nackiger ATmega328P-PU) die Kontrolle über die Rollläden übernimmt und diese mit Hilfe einer Echtzeituhr (RTC DS1307) Jahreszeit-abhängig ansteuert.
Die Tabelle mit der Relation "Monat zu Tageslicht" wird voraussichtlich frühestens in einem Jahr fertig sein.


Schritt 1 - Einlesen der IR-Fernbedienung

Das Einlesen einer IR-Fernbedienung habe ich beim Projekt Glotzregulator ja schon üben können.
Die Schaltung ist trivial:
Arduino mit TSOP     oder auch     Foto Arduino mit TSOP


Allerdings ging es beim Glotzregulator nur um das eindeutige Erkennen der unterschiedlichen Tasten einer IR-Fernbedienung.
Leider hat sich gezeigt, dass das hier nicht langt.
Der dort verwendete Befehl pulseIn() eignet sich nicht, wenn die Zeiten zwischen zwei unmittelbar aufeinander folgenden Flanken gemessen werden sollen.

Daher also ein neuer Ansatz, der in dem Programm getRollotronIRcode.ino umgesetzt ist.
Sobald ein Wechsel von HIGH nach LOW (an einem TSOP31240) erkannt wurde, werden die Zeiten zwischen allen folgenden HIGH-LOW- und LOW-HIGH-Wechseln protokolliert. Und zwar bis zu einer definierten Maximal-Anzahl von Wechseln.

Werden an der IR-Fernbedienung alle elf Tasten je einmal kurz betätigt, liefert das Programm (bei #define DEBUG 1) eine Ausgabe, die ich wegen deren Länge in dieses eigene Dokument ausgelagert habe.

An der Ausgabe ist zu erkennen, dass primär zwei Zeiten vorkommen.
Ein kurzes Signal ist ca. 600µs lang, ein langes Signal ist ca. 3.700µs lang.
Bei Bit 34 wird eine Pause von ca. 29.000µs eingelegt. Danach wiederholt sich das Signal einmalig.
Ebenfalls ist zu erkennen, dass ein Signal offenbar 71 Flanken lang ist.

Werden nur die Zeiten zwischen Flanken betrachtet, dabei ein kurzes Signal mit 0 und ein langes Signal mit 1 dargestellt, ergibt sich folgendes Bild:
01010101010101010101010101010101010-01010101010101010101010101010101010---- A
10100101010101010101010101010101010-10100101010101010101010101010101010---- 1
01011010010101010101010101010101010-01011010010101010101010101010101010---- 2
01010101101001010101010101010101010-01010101101001010101010101010101010---- 3
01010101010110100101010101010101010-01010101010110100101010101010101010---- 4
01010101010101011010010101010101010-01010101010101011010010101010101010---- 5
10100101010101010101010110101010010-10100101010101010101010110101010010---- up
01011010010101010101010101011010101-01011010010101010101010101011010101---- down
10100101101001010101010101010101101-10100101101001010101010101010101101---- +
10101010010110100101010101010101010-10101010010110100101010101010101010---- -
01011010101001011010010101010101010-01011010101001011010010101010101010---- stop

Mit dieser Erkenntnis kann nun ein Programm erstellt werden, das diese Signale ausgibt.


Schritt 2 - Ausgeben der IR-Signale

Das Programm zur Ausgabe der in Schritt 1 gemessenen Signale ist in Version 1.0 erstmal send40KHz.ino.
Es funktioniert - jedoch ist mir noch nicht ganz klar, warum ich die Zeiten (BIT_SHORT und BIT_LONG) korrigieren musste, um die oben ermittelten Werte (BIT_0 und BIT_1 in getRollotronIRcode.ino) zu erhalten.

Mit folgender Schaltung funktioniert das Programm sowohl an einem Arduino mit getRollotronIRcode.ino, als auch an den echten Schwenkwicklern.
Allerdings eignet sich die Schaltung noch nicht, um das gesamte Wohnzimmer von einem Punkt aus mit dem IR-Signal zu fluten ..... und somit alle Schwenkwickler sicher zu erreichen.
Vielleicht hat ja die IR-Fernbedienung nicht umsonst zwei Sende-LEDs eingebaut.....und ein TV-B-Gone sogar vier.

Schaltplan IR Sender V1

Der Transistor an Pin3 schaltet 40KHz auf die LED, der Transistor an Pin4 schaltet das Signal.


Analyse bzgl. Verbesserungsmöglichkeiten

Nun soll mal getestet werden, wie schnell der tone()-Befehl aus der Arduino-CoreLib einschwingt - um ggf. den zweiten Transistor einsparen zu können.
Das Progrämmchen dazu sieht folgendermaßen aus:
void setup() {
  pinMode(2, OUTPUT);
}

void loop() {
  tone(2, 40000);
  delayMicroseconds(77);
  noTone(2);
  delayMicroseconds(33);
}

Es liefert an Pin2 diese Ausgabe (bei 10µs pro Kästchen):
Ozzi-Frequenz

40KHz bedeutet eine Periodendauer von 25µs - hier also 2.5 Kästchen.
Das kommt schon mal recht gut hin.

Nun noch etwas rauszoomen, um das Einschwingverhalten beurteilen zu können:
Ozzi-Einschwingen

Auch das sieht passabel aus.......zumindest bezüglich des Einschwingverhaltens.

Aber bezüglich der Signal- und Pausen-Dauern stimmt das ja vorne und hinten nicht.
Statt 77µs dauert das Signal ca. 200µs und die Pause statt 33µs eher 100µs.

Baue ich das Programm etwas um und ersetze tone() durch digitalWrite():
void setup() {
  pinMode(2, OUTPUT);
}

void loop() {
  digitalWrite(2, HIGH);
  delayMicroseconds(77);
  digitalWrite(2, LOW);
  delayMicroseconds(33);
}

bekomme ich folgendes Bild:
Ozzi-ohneTone

Das passt schon deutlich besser zu den 77µs / 33µs - auch wenn eine Periode eher 120µs als 110µs dauert.
Also verfälscht der tone()-Befehl offenbar den delayMicroseconds()-Befehl.

Schnell noch ein Zweit-Test mit tone() auf einem anderen Pin:
void setup() {
  pinMode(2, OUTPUT);
  tone(4, 40000);
}

void loop() {
  digitalWrite(2, HIGH);
  delayMicroseconds(77);
  digitalWrite(2, LOW);
  delayMicroseconds(33);
}

Liefert wieder Mist:
Ozzi-tonePin4

Tja....damit wäre der Beweis erbracht, dass tone() den delayMicroseconds() stört.
Um so höher die Frequenz von tone(), um so größer die Abweichung bei delayMicroseconds().
So ein Murks...warum steht das nicht GROSS und fett in der Referenz zu tone() oder delayMicroseconds() ???

Aber immerhin wäre damit nun auch erklärt, warum in send40KHz.ino nicht die selben Signal/Pausen-Zeiten wie in getRollotronIRcode.ino verwendet werden konnten.


Erkenntnisse ins Programm einbauen

Das mit der Abweichung von delayMicroseconds() ist zwar unschön, lässt sich aber relativ einfach Workaround'en (siehe send40KHz.ino).
Daher bleibt das vorerst so. Aber der zweite Transistor in der Schaltung wird nicht gebraucht, weil ebenso gut mit mit tone() und noTone() gesendet werden kann.
Für mehr Sendeleistung kommt stattdessen ein deutlich kleinerer Widerstand rein.
Der jetzt durch die IR-LED fließende Strom muss gepulst kommen - sonst wird die IR-LED wahrscheinlich noch einmal ein kurzes Rauchzeichen abgeben und sich dann ins Nirvana verabschieden.
Die neue Version der Schaltung sieht so aus:

Schaltplan IR Sender V2     oder auch      Foto Sender V2

Die Version 2 des Sende-Programms heißt konsequenterweise send40KHz-v2.ino. Und die v2.1 send40KHz-v2.1.ino.
Mit obiger Schaltung steuert sie erfolgreich vier von fünf Schwenkwicklern von einem Standpunkt aus.
Der von diesen am weitesten entfernte Schwenkwickler hatte ca. fünf Meter Abstand zur IR-LED.
Für den fünften musste ich die IR-LED lediglich neu "ausrichten" bzw. die Schaltung etwas drehen.
Dann wurde auch der fünfte ca. sieben Meter entfernte Schwenkwickler erreicht.
Das deutet für mich darauf hin, dass diese IR-LEDs ziemlich gerichtet strahlen.
Voraussichtlich werde ich mindestens zwei Sende-Einheiten bauen und strategisch platzieren.

Da sich auch ein paar IR-Dioden vom Typ CQY-99 in meinen Beständen befanden (und diese laut Datenblatt weniger gerichtet strahlen), habe ich noch etwas damit rumgetestet.
So ganz zufriedenstellend war das aber immer noch nicht.
Deshalb habe ich dann noch etwas "Strom nachgelegt".
Weil der nun aber nicht mehr vom ArduinoUNO kommen konnte, habe ich ein Akku-Pack angeschlossen.
Sah dann so aus:
Foto Sender V3    oder auch    Schaltplan Sender V2a
Nun endlich.... :-)
Alle fünf Schwenkwickler können von einer Position aus angesprochen werden, ohne die IR-LED zwischendurch neu ausrichten zu müssen.
Unter der Annahme, dass über der IR-LED 1.7V abfallen und das Akku-Pack 5.4V liefert, fließen bei einem Vorwiderstand mit 4.1Ω durch die IR-LED (5.4V-1.7V)/4.1Ω=0.9A.
Das ist zwar neun mal mehr als dauerhaft erlaubt, aber noch deutlich unterhalb des für 100µs lang erlaubten Stroms von 1.5A.
So rein theoretisch bzw. nach Datenblatt müsste ich bis (5.4V-2.7V)/1.5A=1.8Ω runtergehen dürfen, wenn ich das maximal 100µs lang mache.
Aber ich will die IR-LED ja nicht am äußersten Limit betreiben....und lieber länger was davon haben.
BTW: der MJE3055 ist ein bischen Overkill. Der schafft 6A. Aber weil ich von dem ein paar rumliegen habe, ist es der geworden.


Schritt 3 - Zeitsteuerung zufügen

Eine RTC ist schnell angeschlossen. Damit würde das Programm dann die Uhrzeit und das Datum bzw. den Tag im Jahr kennen.
Sofern ich eine Liste im Internet fände, die mir die Uhrzeit des Sonnenaufgangs und -untergangs in Schleswig-Holstein der letzten 12 Monaten liefert, könnte ich das Programm einmalig damit füttern....und gut wäre es.
Ansonsten brauche ich eine Möglichkeit, die Schaltung (z.B. per WLAN) zu erreichen - um sie über das Jahr mit neuen Zeiten füttern zu können.
Oder einen Sensor im Außenbereich, der mir die Arbeit abnimmt.

Ich such erst mal nach so einer Liste ......... schwuppdiwupp ....... das war ja unerwartet schmerzfrei.
Google ist fraglos evil, aber die Suchergebnisse sind klasse.
Unter http://www.sunrise-and-sunset.com/de/sun/deutschland/buchen__10 steht alles, was ich brauche.

Da nehme ich mal 2000 und 2014 von der Web-Seite und wähle entsprechende Zeiten für meine Tabelle aus:
Monat
Jahr 2000
Jahr 2014
Normalzeit
Ortszeit
Januar
01.01. 08:33 16:08 08:33 16:09 08:00 17:00 08:00
17:00
Januar 15.01.
08:25 16:28 08:24 16:29 08:00
17:00
08:00
17:00
Februar 01.02. 08:02 16:59 08:01 17:00 07:30 17:30 07:30
17:30
Februar 15.02.
07:36 17:27 07:34 17:28 07:00
18:00
07:00
18:00
März
01.03. 07:03 17:56 07:03 17:55 06:30 18:30 06:30
18:30
März 15.03.
06:29 18:22 06:30 18:21 06:00
19:00
06:00
19:00
April
01.04. 05:48 18:53 05:49 18:53 05:00 19:30
06:00
20:30
April 15.04.
05:15
19:19
05:16 19:18
05:00
20:00
06:00
21:00
Mai
01.05. 04:40 19:48 04:41 19:47 05:00 20:30
06:00
21:30
Mai 15.05.
04:15
20:12
04:16
20:11
05:00 20:30
06:00 21:30
Juni
01.06. 03:54 20:36 03:54 20:36 05:00 21:00
06:00
22:00
Juni 15.06.
03:47
20:48
03:47
20:48
05:00 21:30
06:00 22:30
Juli
01.07. 03:53 20:49 03:53 20:49 05:00 21:30 06:00
22:30
Juli 15.07.
04:08
20:38
04:08
20:38
05:00 21:30
06:00 22:30
August
01.08. 04:34 20:12 04:34 20:13 05:00 21:00 06:00
22:00
August 15.08.
04:58
19:45
04:57
19:45
05:00 20:30
06:00 21:30
September
01.09. 05:28 19:06 05:27 19:07 05:00 20:00 06:00
21:00
September 15.09.
05:52
18:32
05:51
18:33
05:30 19:30
06:30 20:30
Oktober
01.10. 06:20 17:53 06:20 17:54 06:00 19:00 07:00
20:00
Oktober 15.10.
06:46
17:20
06:45
17:21
06:00 18:00
07:00
19:00
November
01.11. 07:18 16:44 07:17 16:44 06:30 17:30 07:00
17:30
November 15.11.
07:44 16:19 07:43 16:20 07:00
17:00
07:00
17:00
Dezember
01.12. 08:11 16:02 08:10 16:02 07:30 16:30 07:30
16:30
Dezember 15.12.
08:27 15:58 08:27 15:58 08:00
16:30
08:00
16:30
Weil die RTC Sommer- und Winterzeit von sich aus nicht berücksichtigt, wird sie konstant in der Winter- bzw. Normalzeit laufen.
Daher sind die in der Sommerzeit liegenden Werte entsprechend anzupassen (die blau dargestellten Zeiten sind angepasst).

Das Programm dafür sieht folgendermaßen aus:
#include <Wire.h>
#include <DS1307new.h>  // http://code.google.com/p/ds1307new/

#define UP_HOUR   0
#define UP_MINUTE 1
#define DN_HOUR   2
#define DN_MINUTE 3
const int RUN_AT[12][2][4] = {
  // erste Dimension : 12 Monate
  // zweite Dimension: 0=Tag im Monat zwischen 1-14, 1=Tag im Monat zwischen 15-31
  // dritte Dimension: rauf_stunde, rauf_minute, runter_stunde, runter_minute
  // alle Zeiten sind in Winter- bzw. Normalzeit!
  {  {  8,  0, 17,  0 }, {  8,  0, 17,  0 }  },   // 01 - Januar    08:00-17:00  08:00-17:00
  {  {  7, 30, 17, 30 }, {  7,  0, 18,  0 }  },   // 02 - Februar   07:30-17:30  07:00-18:00
  {  {  6, 30, 18, 30 }, {  6,  0, 19,  0 }  },   // 03 - März      06:00-18:30  06:00-19:00
  {  {  5,  0, 19, 30 }, {  5,  0, 20,  0 }  },   // 04 - April     05:00-19:30  05:00-20:00
  {  {  5,  0, 20, 30 }, {  5,  0, 20, 30 }  },   // 05 - Mai       05:00-20:30  05:00-20:30
  {  {  5,  0, 21,  0 }, {  5,  0, 21, 30 }  },   // 06 - Juni      05:00-21:00  05:00-21:30
  {  {  5,  0, 21, 30 }, {  5,  0, 21, 30 }  },   // 07 - Juli      05:00-21:30  05:00-21:30
  {  {  5,  0, 21,  0 }, {  5,  0, 20, 30 }  },   // 08 - August    05:00-21:00  05:00-20:30
  {  {  5,  0, 20,  0 }, {  5, 30, 19, 30 }  },   // 09 - September 05:00-20:00  05:30-19:30
  {  {  6,  0, 19,  0 }, {  6,  0, 18,  0 }  },   // 10 - Oktober   06:00-19:00  06:00-18:00
  {  {  6, 30, 17, 30 }, {  7,  0, 17,  0 }  },   // 11 - November  06:30-17:30  07:00-17:00
  {  {  7, 30, 16, 30 }, {  8,  0, 16, 30 }  },   // 12 - Dezember  07:30-16:30  08:00-16:30
};
int last_up_day_g=0, last_dn_day_g=0;

[....]

void loop() {
  int rtc_month, rtc_day;

  RTC.getTime();
  rtc_month=RTC.month-1;  // Index in RUN_AT[x][][]
  rtc_day=(RTC.day>=15);  // Index in RUN_AT[][x][]
  if(rtc_month>=0 && RTC.month<=11) {
    // RTC.month ist valide
    if(RTC.hour==RUN_AT[rtc_month][rtc_day][UP_HOUR] && RTC.minute==RUN_AT[rtc_month][rtc_day][UP_MINUTE])  {
      // aktuelle Zeit passt
      if(RTC.day!=last_up_day_g)  {
        // heute noch nicht hochgefahren
        last_up_day_g=RTC.day;
        open_shutter();
      }
    } else if(RTC.hour==RUN_AT[rtc_month][rtc_day][DN_HOUR] && RTC.minute==RUN_AT[rtc_month][rtc_day][DN_MINUTE])  {
      // aktuelle Zeit passt
      if(RTC.day!=last_dn_day_g)  {
        // heute noch nicht runtergefahren
        last_dn_day_g=RTC.day;
        close_shutter();
      }
    }
  }

 
[....]

}



Schritt 4 - weitere Überlegungen...


...zum Stromverbrauch

Ich brauche noch ein Verfahren, mit dem ich die CPU in den Tiefschlaf schicken kann.
Schließlich soll das Ding ja nur wenige Sekunden pro Tag was tun - und möglichst auch nur dann Strom verbrauchen.
Also wird Google mal nach "arduino low power" befragt.
Der zweite Treffer sieht schon mal gut aus.
Darin wird diese URL referenziert, unter der sich genau das findet, was ich brauche.
Von 15.15mA (für den nackigen ATmega328) auf 0.35µA ist doch mal eine Ansage.
Speziell diese Erkenntnis ist interessant:
  • All pins as outputs, and LOW: 0.35 µA (same as before).
  • All pins as outputs, and HIGH: 1.86 µA.
  • All pins as inputs, and LOW (in other words, internal pull-ups disabled): 0.35 µA (same as before).
  • All pins as inputs, and HIGH (in other words, internal pull-ups enabled): 1.25 µA.

Aber wenn ich nun schon in den µA-Bereich komme, stellt sich die Frage, wie ich die RTC anschließen muss, um auch dort von 1.5mA (active) auf den Batterie-Betriebsmodus mit <1µA zu kommen, obwohl die 5V Stromversorgung für die CPU konstant angeschlossen bleibt.
Und idealerweise, ohne eine zusätzliche 3V Batterie auszukommen. Obwohl .... eine zusätzliche 3V Batterie hätte den Vorteil, den Akku für den ATmega328 wechseln zu können, ohne danach die Uhr neu stellen zu müssen.
Wenn der DS1307 im Betrieb nur 1.5mA zieht, sollte ich seine Stromversorgung (an Pin 8 des DS1307) direkt über einen Ausgang vom ATmega328 speisen können.
Bei LOW läuft er über seine Batterie (und zieht laut Datenblatt 500nA), bei HIGH ist er an und kann nach Datum und Uhrzeit befragt werden.
Und erfreulicherweise ist er sogar unmittelbar nach HIGH bereit bzw. braucht keinen delay() zwischen Einschalten und Abfrage.
Folgendes klappt auch mit auskommentiertem delay-Befehl:
digitalWrite(DS1307_POWER_PIN, HIGH); // RTC "einschalten"
//delay(3);
RTC.getTime();
digitalWrite(DS1307_POWER_PIN, LOW);  // RTC "ausschalten"
Der Stromverbrauch an Pin 8 des DS1307 ist bei LOW 0µA und bei HIGH 107µA (oder kurzfristig bei RTC.getTime() vielleicht auch etwas mehr).
Sehr schön :-) Und LOW am ATmega328 ist ja auch genau das, was man (gemäß obiger Erkenntnisse) im Tiefschlaf haben will.

...zum Stellen der Uhr

Wie die Uhr gestellt werden kann, wenn ein ATmega328 statt eines ArduinoUNO (mit USB-Port) zum Einsatz kommt, ist noch offen.
Natürlich könnte man temporär einen ArduinoUNO an SDA und SCL von der RTC anschließen und darüber die Uhr initial einstellen.
Angeblich soll das Ding mit einer 3V Knopfzelle über 10 Jahre laufen. Müsste man also nicht oft machen. Aber schön wäre sowas nicht.

Ein DCF77-Modul wäre denkbar. Auf den Stomverbrauch würde sich das allerdings ziemlich negativ auswirken. Speziell bei schlechtem Empfang.

Richtig edel hingegen wäre es, wenn man über einem ESP8266 mit dem ATmega328 sprechen könnte.
Dann ließe sich ggf. sogar obige Tabelle mit den Schaltzeiten nachträglich anpassen, ohne den Chip neu flashen zu müssen.
Weil das Teil aber ordentlich Strom zieht, darf es ebenfalls nur bei Bedarf aktiviert werden.
Auch braucht es wohl einige Zeit, bis es sich im WLAN eingebucht hat.

Ein denkbarer Ansatz wäre ein Schalter am Gerät. Wenn der eingeschaltet ist, geht der ATmega328 nicht sofort wieder in den Tiefschlaf, nachdem er erkannt hat, dass derzeit nix für ihn zu tun ist. Stattdessen verzweigt er in eine Routine zur Abfrage des ESP8266 (der jetzt mit Strom versorgt wird) und wartet auf Anweisungen über WLAN ... so lange, bis der Schalter wieder abgeschaltet wird. Dabei würde es voraussichtlich nicht mal stören, wenn während dieser Zeit die Rollläden nicht berücksichtigt würden.

Sicherer wäre jedoch ein Taster, der für die z.B. nächsten 10 Minuten den Tiefschlaf verhindert, den ESP8266 mit Strom versorgt und bei jedem Befehl über WLAN den 10-Minuten-Timeout neu triggert. Dann könnte man gar nicht erst vergessen, den Schalter wieder auszuschalten.
Weil es sehr wahrscheinlich wäre, dass der Taster während einer Tiefschlafphase betätigt wird, bräuchte man noch eine LED, die den Programmiermodus am Gerät anzeigt. Die Tiefschlafphase kann maximal acht Sekunden dauern. Im schlimmsten Fall müsste man den Taster also acht Sekunden lang gedrückt halten - bis die LED den Programmiermodus anzeigt. Ab dann hätte man 10 Minuten Zeit, zu einem Rechner zu gehen und das Kommunikations-Programm zu starten.
Auch wäre denkbar, dass auf irgend einem Rechner ein Server-Dienst läuft, mit dem sich der ATmega328 einmal täglich verbinden kann um darüber seine Uhr (gemäß NTP) korrigieren zu können.
Das hört sich doch gut an......so wirds gemacht. Nun muss ich erstmal auf die Lieferung des ESP8266 warten.
Und nächstes Wochenende kann ich dann [hoffentlich] ein bischen damit rumtesten.


Schritt 5 - ESP8266 (vor der Lieferung)

Weil ich eh noch auf die Lieferung des ESP8266 warten muss, kann ich ja schon mal Doku zusammentragen:
Hersteller-Doku
http://bbs.espressif.com/
Module Quick Start Guide
http://rancidbacon.com/files/kiwicon8/ESP8266_WiFi_Module_Quick_Start_Guide_v_1.0.4.pdf
Specs (confidential...hihi)
http://www.esp8266.com/wiki/lib/exe/fetch.php?media=esp8266_specifications_english.pdf
AT-Befehle und Pinout http://fkainka.de/kurzinfos-zum-wlan-modul-esp8266-wi07c/
Firmware-Update
https://ukhas.net/wiki/esp8266_firmware_update
Kolban's book on the ESP8266
http://neilkolban.com/tech/wp.../Kolbans-Book-on-the-ESP8266-October-2015.pdf

Die Pegel-Wandlung von 5V nach 3V für den RX-Pin kann mit zwei Widerständen im Verhältnis 2/3 (z.B. 2.2KΩ und 3.3KΩ) realisiert werden.
Also:
   ArduinoPin --- R1(2.2KΩ) ---+--- R2(3.3KΩ) --- GND
                               |
                               +--- ESP8266-RX-Pin

Damit kommen 5V/(2200Ω+3300Ω)*3300Ω= 3V am RX-Pin an - das sollte reichen.

Die 3.3V-Stromversorgung für den ESP8266 sollte sich dadurch gewinnen lassen, dass einfach zwischen den vier 1.2V-Akkus abgegriffen wird.
Wenn die vier Mono-Zellen voll sind, liefern sie 5.4V. Über zwei Akkus messe ich 2.7V. Laut Specs funktioniert der Chip zwischen 1.7V - 3.6V.

Damit sähe der bisherige Gesamt-Schaltplan folgendermaßen aus:
Schaltplan

Der ESP8266 kann über Q2 und R7 an- oder ausgeschaltet werden. Die Pegelanpassung für RX erfolgt über R3 und R4.
Der Taster S1 ist für den Reset des ATmega328 da (und kann final entfallen).
Der Taster S2 ist für den Programmiermodus, der mit LED1 und R2 angezeigt wird.
Die Sende-Einheit besteht aus Q1, LED2, R5 und R6.

Bei den IO-Pins (D2-D13) am ATmega328 habe ich übrigens einfach die ausgewählt, die gerade in der Nähe waren.
Sie werden sich sicher noch ändern bzw. passen höchstens zufällig zu den #define-Statements im Programm-Code.
Lediglich die RTC ist wegen SDA/SCL auf A4/A5 festgelegt.

Auf folgender Seite findet sich übrigens das Pin-Mapping ArduinoUNO / ATmega328.


Überlegungen zur Kommunikation mit der Rollladensteuerung

Auf einem Linux-System gw (im VLAN42) lauscht 24x7 ein Python-Script auf einem Port und wartet auf Anfragen von der Rollladensteuerung.
Das Script erhält seine Zeiten-Tabelle aus einer Datei. Diese Datei kann per Editor oder über ein weiteres Python-Script mit wxWidgets-GUI geändert werden.
Daraus abgeleiteter Ansatz:
Bei dieser Vorgehensweise wird der Schalter für den Programmier-Modus nicht mehr benötigt.
Änderungen können jederzeit vorgenommen, ab der nächsten Kontakt-Aufnahme von Seiten Rollladensteuerung sind sie dann aktiv.

Irgend eine Art Authentifizierung oder Crypto muss wegen des offenen WLANs noch rauf...
Z.B.: Python-Script und Arduino-Code kennen ein gemeinsames Passwort.
Das Python-Script schickt dem ATmega328 Datum und Uhrzeit als Klartext-String.
Danach dann den MD5-Hash (https://github.com/tzikis/ArduinoMD5/) von Datum+Uhrzeit+Passwort.
Also etwa für "2015-10-14 20:40:22" und "Passw0rt" auf Python-Seite:
    >>> import hashlib
    >>> hashlib.md5("2015-10-14 20:40:22 Passw0rt").hexdigest()
    'f9dbca6a27bbab49dbb398b406872cb7'
Und auf ATmega328-Seite:
    #include <MD5.h>
    [...]
    unsigned char* hash=MD5::make_hash("2015-10-14 20:40:22 Passw0rt");
    char *md5str = MD5::make_digest(hash, 16);
    Serial.println(md5str);

liefert im Terminal-Fenster: f9dbca6a27bbab49dbb398b406872cb7

Wobei Datum+Uhrzeit eigentlich keine ganz so gute Idee ist.
Wenn zu festen Zeiten kommuniziert wird, sind es immer wieder die selben Zeiten. Und damit die selben Hashes.
Eine hinreichend große Zufallszahl wäre weniger vorhersagbar.
Ablauf ATmega328 Ablauf Python-Script @ gw

Verbindung zu gw+port# herstellen
Zufallszahl empfangen
MD5 aus Zufallszahl + PasswortP bilden
MD5 senden


Zufallszahl generieren
MD5 aus Zufallszahl + PasswortA bilden
MD5 empfangen
wenn sie nicht zur eigenen MD5 passt
    Verbindung schließen
Uhrzeit senden

Uhrzeit empfangen
Uhrzeit in RTC einstellen
Zeiten-Liste senden

Zeiten-Liste empfangen
Verbindung schließen
Socket öffnen und auf Connects warten
bei Connect
    Zufallszahl generieren und senden
    MD5 aus Zufallszahl + PasswortP bilden
    MD5 empfangen
    wenn sie nicht zur eigenen MD5 passt
        Verbindung schließen
    Zufallszahl empfangen
    MD5 aus Zufallszahl + PasswortA bilden
    MD5 senden


    Uhrzeit empfangen
    Uhrzeit zur Info speichern
    Uhrzeit senden

    Zeiten-Liste empfangen
    Zeiten-Liste zur Info speichern
    ggf. geänderte Zeiten-Liste senden
    Verbindung schließen
Zusätzlich sollte jede Seite sein eigenes Passwort haben, aber das des Anderen natürlich auch kennen.
Bei nur einem Passwort für beide Richtungen könnte sonst theoretisch jemand dadurch Schabernack treiben, dass er so Man-In-The-Middle-mäßig das Python-Script nach dem Hash befragt, für den er selber gerade vom ATmega328 befragt wurde.


Schritt 6 - die ESP8266-Module sind da

Es ist Samstag. Früher Nachmittag. Endlich wurden die zehn bestellten ESP8266-Module geliefert.

Erste Erkenntnisse:
Es ist Sonntag, 13:00 Uhr und ich habe zig graue Haare mehr. Und alles nur wegen eines verschissenen Steckernetzteils, das offenbar keine sauberen 3.3V oder zu wenig Saft (angeblich sollte es 600mA bringen) geliefert hat.
Immer wieder gabs ASCII-Salat. In der letzten Stufe nur noch bei solchen Funktionen, die WLAN-Kommunikation bewirkten (bei denen also etwas mehr Strom verbraten wird). Dann bin ich endlich auf die Idee gekommen, das richtige Labornetzteil auf 3.3V einzustellen, um darüber den ESP8266 mit Strom zu versorgen. Und endlich kamen auch über den ArduinoUNO die Ergebnisse, die ich bis dahin nur gesehen hatte, wenn der ESP8266 am "USB zu TTL-Konverter" hing.

ESP8266 am ArduinoUNO

Es ist Sonntag, 20:30 Uhr. Der ArduinoUNO spricht über den ESP8266 mit dem Python-Script :-)
Anbei das Log beider Seiten (mit ein paar manuell eingefügten Leerzeilen):
Log ATmega328 Log Python-Script @ gw


connected

sent:AUTH_REQ


data=230546794

sent:095bb47d0fbd8691edcb25e6b9b43292

data=AUTHORIZED

sent:100016807
data=68ca8e9f6cb5d38b95619519ce473a18
own =68ca8e9f6cb5d38b95619519ce473a18

sent:AUTHORIZED
data=FINE
authorized

sent:TIME_INFO
data=OK

sent:1967.06.11 01:02:03

data=OK

sent:GET_TIME
data=2015.10.18 19:34:01


dede@gw:~$ ./SocketServerESP8266.py
SocketServer started on port 2626
Client joined 192.168.42.101:2020

received: AUTH_REQ

sende challenge
sent: 230546794

received: 095bb47d0fbd8691edcb25e6b9b43292
my md5 =  095bb47d0fbd8691edcb25e6b9b43292
sent: AUTHORIZED

received: 100016807
sent: 68ca8e9f6cb5d38b95619519ce473a18


received: AUTHORIZED
sent: FINE


received: TIME_INFO
sent: OK

received: 1967.06.11 01:02:03
Time-Info: 1967.06.11 01:02:03
sent: OK

received: GET_TIME
sent: 2015.10.18 19:34:01
Client left 192.168.42.101:2020


Die zugehörigen Programme sind diese hier:
    ESP8266-AnsteuerungV2.ino
und
    SocketServerESP8266.py

Der Arduino-Compiler meldet, dass das Programm 18.748 Bytes (58%) des Programmspeicher und 772 Bytes (37%) des dynamischen Speichers belegt.
Es kommt zwar noch die Library für die RTC dazu, aber das sollte schon passen.

Jetzt habe ich mir erstmal mein zweites Bierchen verdient......


Grundeinrichtung des ESP8266

Damit der ESP8266 mit der Schaltung bzw. dem Programm funktioniert, waren zunächst ein paar Defaultwerte einzustellen.
Ich habe das mit einem USB-zu-TTL-Konverter gemacht.
Als Terminalprogramm habe ich CuteCom mit folgenden Einstellungen verwendet:
CuteCom

Die Baudrate war bei meinen Modulen ab Werk auf 115200 eingestellt.
Wenn man seinen Linux-User der Gruppe "dialout" zuweist, darf man unter openSUSE auf /dev/ttyUSB0 zugreifen, ohne das Programm mit  gksu cutecom  starten zu müssen.

Befehl
Funktion
[mögliche] Antwort
AT+CWMODE_DEF=1 "station mode" einstellen AT+CWMODE_DEF=1


OK
AT+CWLAP verfügbare WLANs anzeigen AT+CWLAP

+CWLAP:(0,"FreifunkWees01.2 (http://ffw)",-79,"64:66:b3:fa:ba:c9",1)

OK
AT+CWJAP_DEF="FreifunkWees01.2 (http://ffw)","" mit WLAN verbinden AT+CWJAP_DEF="FreifunkWees01.2 (http://ffw)",""

WIFI CONNECTED
WIFI GOT IP

OK
AT+CWAUTOCONN? prüfen, ob Autoconnect (=1) an ist AT+CWAUTOCONN?

+CWAUTOCONN:1
OK
AT+CIPMUX? prüfen, ob "single connection" (=0) an ist AT+CIPMUX?

+CIPMUX:0

OK
AT+UART_DEF=38400,8,1,0,0 Baudrate einstellen
AT+UART_DEF=38400,8,1,0,0


OK

rausziehen + kurz warten + wieder reinstecken

AT+CWJAP? prüfen, ob sich das Modul automatisch verbindet AT+CWJAP?

+CWJAP:"FreifunkWees01.2 (http://ffw)","64:66:b3:fa:ba:c9",1,-84

OK
AT+CIFSR IP-Adresse anzeigen lassen AT+CIFSR

+CIFSR:STAIP,"192.168.42.102"
+CIFSR:STAMAC,"18:fe:34:da:63:d0"

OK


das Programm ist quasi fertig

Mittlerweile habe ich meinem Test-Aufbau die RTC zugefügt und damit alle Komponenten beisammen, die ich im Programm steuern muss.
Lediglich der Transistor zum Abschalten des ESP8266 fehlt noch. Ob ich, wie im vorigen Abschnitt angedacht, den Reset-Pin des ESP8266 vom ATmega328 ansteuerbar mache, hängt davon ab, wie sich das Teil verhält, wenn es über den Transistor ein und ausgeschaltet wird.
Ach...und die IR-Sendeeinheit fehlt auch noch (die mir hier im Büro aber eh nix nützen würde, weil in diesem Raum nur ein einfacher bzw. nicht fernsteuerbarer Rollotron Schwenkwickler seinen Dienst erfüllt).

Beim Python-Script ist hinzugekommen:
  1. Allen über das WLAN zu übertragenden Daten wird eine Prüfsumme angehängt. (ich wollte sicher sein können, dass keine irren Zeiten für das rauf- oder runterfahren beim Programm ankommen und ausgeführt würden...und ASCII-Salat habe ich ja schon einigen ankommen sehen).
  2. Eine Datei mit Zeiten für das rauf und runterfahren wird gelesen und (nach Änderungs-Erkennung) zum ATmega328 gesendet.
Auf der ATmega328-Seite ist hinzugekommen:
  1. Allen über das WLAN zu übertragenden Daten wird eine Prüfsumme angehängt.
  2. Der Tiefschlaf-Modus ist implementiert (aber noch nicht bzgl. Strom-Aufnahme überprüft - ist ja eh noch ein ArduinoUNO).
  3. Die bisher einzeln entwickelt und getesteten Teil-Funktionen (IR-Senden, Zeiten-Tabelle lesen, RTC ansteuern) sind ins Hauptprogramm aufgenommen.
Für die Stromversorgung habe ich mir überlegt, vier 1.2V Akkus einzusetzen.
Für VBAT der RTC wird über zwei Akkus abgegriffen (2.4V), für das ESP8266-Modul über drei Akkus (3.6V) und für die restliche Schaltung über allen vier Akkus (4.8V). Mal schauen, wie sich die unterschiedliche Belastung der Akkus auswirkt...
Ein Akku-Wechsel führt bei dieser Art der Stromversorgung zwar dazu, dass die RTC keine Puffer-Batterie mehr hat, aber das macht ja nix, weil sie beim ersten Start nach Akku-Wechsel sofort über WLAN neu gestellt wird.

Auch wenn der Taster (oben S2) für den Programmier-Modus nicht mehr benötigt wird, soll die zugehörige LED gerne drin bleiben und alle acht Sekunden einmal kurz für 50mS aufleuchten - um damit anzuzeigen, dass der ATmega328 noch brav seine Tiefschlaf-Loop abarbeitet.

Eine kleine Erweiterung der Schaltung könnte sein, dass der Akku-Ladestand bzw. die drei Spannungen überwacht werden, um dann ggf. bei der nächsten WLAN-Kommunikation eine entsprechende Warnung senden zu können.


Rückschläge und Erweiterungs-Ideen

Schon kommt der nächste Frust.

Das Abschalten des ESP8266 mit Transistor funktioniert nicht. Oder genauer gesagt, der "Betrieb über einen Transistor" hat nicht funktioniert.
Stattdessen kann man aber den PD-Pin auf LOW setzen. Dabei zieht das Modul dann 0.5mA. Laut Specs von dem Chip sollten es aber 0.5µA sein.
Das wird wohl an der kleinen (roten) LED liegen, die immer leuchtet, sobald das Ding am Strom hängt.
Nach Ab-Löten der roten LED sind es bei PD=LOW 34µA und bei PD=HIGH 142mA (oder mehr).
Naja...34µA ist deutlich besser als 0.5mA - aber auch noch lange nicht 0.5µA. Dann wird wohl dieser zweite Chip die Differenz ziehen.

Und als nächstes: als ich das auf 3.3V eingestellte Labornetzteil durch ein Akku-Pack aus drei Mignon-Akkus (Eneloop, 1.900mAh) ersetzt habe, ging beim ESP8266 erstmal nix mehr. Mit einem 0.1F (SuperCap) Kondensator parallel zum Akku-Pack wurde es gefühlt etwas besser. Aber immer noch sehr unbefriedigend.
Dann habe ich einen mit Mono-Zellen (tecxus, 10.000mAh) bestückten Akku-Pack probiert. Wieder etwas besser, jedoch immer noch deutlich schlechter, als am Labornetzteil.
Ich verstehe das nicht. Die Spannung stimmt halbwegs und Strom sollte aus so einem Akku doch wohl reichlich rauskommen...!?
Es könnte was mit den Pegeln zwischen ESP8266 und ArduinoUNO zu tun haben. Auf ping antwortet der ESP8266 nämlich mehr oder weniger brav.

Ich habe mir gerade mal fünf Pegel-Wandler-Module bestellt.
In der Bestätigungs-Email wurde dann allerdings eine Lieferzeit von zwei bis vier Wochen genannt.
So ein Scheiß. Habe ich wieder nicht richtig aufgepasst.
Der Händler hieß "März Gerste" und hatte die Dinger "auf Lager". Erst nach der Bestellung habe ich nachgesehen...und feststellen müssen, dass die Dinger (trotz Umlaut im Namen des Händlers) dann jetzt wohl aus China geliefert werden. Daher also zwei bis vier Wochen Lieferzeit. Dreck! Man muss sich doch zwingen, vor einer Bestellung nicht nur auf den Tag des voraussichtlichen Liefertermins, sondern auch auf dessen Monat zu gucken.
Weil ich nicht so lange warten wollte, musste ich also nochmal auf die Suche gehen. Der hier ist es geworden. Kostet zwar mehr als das Dreifache, aber soll morgen geliefert werden (...aber nur, weil ich mal wieder eine Amazon-Prime-Probe-Mitgliedschaft abgeschlossen habe :-) ).
...hat übrigens nicht geklappt mit der Lieferung am nächsten Tag.

Jetzt mache ich erst mal eine kleine Test-Reihe.
Und zwar mit Labornetzteil und Pegel-Wandlung bei den Empfangs-Pins (bzgl. ESP8266-Seite) mit Widerständen (2.2KΩ / 3.3KΩ).
Ein Testlauf wird mit dem Reset-Taster am ArduinoUNO eingeleitet. Dadurch wird der PD-Pin am ESP8266 kurz auf LOW gezogen, dieser dann auf HIGH gesetzt, 15 Sekunden gewartet, danach per AT-Befehl der Chip testhalber befragt und dann der Datenaustausch via TCP/IP gestartet.
Vcc ESP8266
Antwort auf ping
Reaktion auf AT-Befehle
Datenaustausch mit dem Python-Script
2.2V
nein
keine Reaktion nicht möglich
2.4V
ja (viele DUP's)
ASCII-Salat nicht möglich
2.5V
ja
ASCII-Salat nicht möglich
2.7V
ja
korrekt
braucht mehrere Versuche
2.8V
ja
korrekt
braucht mehrere Versuche
3.0V
ja
korrekt braucht manchmal mehrere Versuche
3.3V
ja
korrekt stabil
3.5V
ja
korrekt stabil
3.7V
ja
korrekt
braucht manchmal mehrere Versuche
3.9V
ja
korrekt
stabil
4.0V
ja (viele DUP's) korrekt
stabil

Bei 4.0V wurde der Chip schon deutlich warm.
Noch höher wollte ich daher lieber nicht gehen, um das Modul nicht doch noch irgendwann wegzugrillen.

BTW: mit "viele DUP'S" meine ich sowas hier:
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=3.75 ms
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=16.9 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=17.0 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=17.0 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=17.0 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=147 ttl=255 time=17.0 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=148 ttl=255 time=3.55 ms
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=148 ttl=255 time=4.56 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=148 ttl=255 time=8.10 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=148 ttl=255 time=11.2 ms (DUP!)
64 bytes from ESP8266-2.FreifunkWees01.local (192.168.42.102): icmp_seq=148 ttl=255 time=12.9 ms (DUP!)


Fazit: ohne richtige Pegelwandlung liegt der beste Betriebsbereich zwischen 3.0V und 3.5V.
Notfalls gehts ab 2.7V und bis zu 3.9V (zumindest, sofern kein Dauerbetrieb des ESP8266 geplant ist).
Da ist das Ding doch pingeliger, als ich gedacht hatte.
Ich bin ja mal gespannt, wie die Liste aussehen wird, wenn die Kommunikation über ein Pegel-Wandler-Modul läuft.

Eine Auffälligkeit bei den einzelnen Test-Durchläufen war noch, dass ich trotz der exorbitanten Wartezeit von 15 Sekunden nach ChipEnable den Eindruck hatte, als würde das Ding mit der Zeit besser reagieren. Also nach Änderung der Spannung war es erstmal eine gefühlte Minute recht gruselig, wurde dann besser und blieb so. Bis zur nächsten Spannungs-Änderung.


Erst wird jetzt aber nochmal der Test mit den Akku-Packs wiederholt.
Schließlich habe ich neue Erkenntnisse und eine Programm-Version im ArduinoUNO, die mir "Kommunikations-Stabilitäts-Infos" ausgibt.
Ich lade erstmal beide Akku-Sätze frisch auf. Der Spannungsverlust bei relativ geringer Last ist doch schon sonderbar.
Aber wenn die Schaltung nur mit frisch geladenen Akkus funktionieren sollte, würde mir das auch herzlich wenig nützen.
Häufiger als einmal pro Jahr wollte ich die nicht wechseln müssen.... Zumal ich derzeit keinen zweiten Vierer-Satz Mono-Zellen hätte.

Nun ist der Akku-Pack mit den Mono-Zellen randvoll geladen.
Liefert über zwei Akkus 2.75V, über drei 4.13V und über allen vier 5.51V. Das passt ja überhaupt nicht.

Vorsichtshalber werde ich mal ein paar 3.3V Festspannungsregler bestellen. Damit kann ich dann testen, wie sich das alles verhält, wenn ich die Schaltung mit einem Stecker-Schaltnetzteil mit 5V versorge und mir die 3.3V für den ESP8266 und die VBAT der RTC über so einen Regler abzweige.
Dabei muss sich dann zeigen, was das Steckernetzteil im Leerlauf so vernascht. Selbst wenn es nur ein Watt sein sollte, macht das immer noch 1W*24h*365 = 8.8kWh bei Betrieb rund um die Uhr. Somit jährliche Stromkosten von knapp 2€. Naja, könnte ich notfalls verkraften.

Und nach einem Stromausfall würde sich die Schaltung ganz automatisch wieder auf Stand bringen. Evt. sollte in der setup()-Routine für diesen Fall noch etwas länger gewartet werden. Damit auch der Rechner mit der Gateway-VM hoch ist, alle VMs hoch sind und die WLAN-Router sich auch wieder wohlfühlen und ansprechbar sind.

Dabei fällt mir gerade ein, dass der Load der Zeiten aus dem EEPROMs ins RAM zwar mit Prüfsumme abgesichert ist, aber nicht das Bedienen der Rollläden sperrt, wenn die Prüfsumme nicht zu den Daten gepasst hat. Stattdessen würden die im Programm festgelegten Daten verwendet werden. Ganz falsch sind die ja nicht. Trotzdem könnte die Zeiten-Tabelle mehrfach im EEPROM abgelegt werden. Platz wäre da noch. Erst wenn das EEPROM die Grätsche macht, würde auf die Zeiten-Tabelle aus dem Programm-Speicher zurückgefallen werden. Und das auch nur, wenn nicht mit dem Python-Script synchronisiert werden konnte.

Auch könnte es nicht schaden, die RTC regelmäßig auf korrekte Funktion zu prüfen. Vorzugsweise dann, wenn sie gerade neu gestellt wurde. Nach Stellen der Uhr könnte die gestellte Zeit gemerkt werden, eine Tiefschlafphase von acht Sekunden eingelegt werden - um danach zu prüfen, ob die RTC zwischen sieben bis neuen Sekunden weiter gelaufen ist. Wenn nicht, Info via WLAN an das Python-Script.
Aber was wäre in diesem Fall mit den Rollläden?
Nicht mehr ansteuern?
Nicht so gut.
Oder die Zeit über WLAN holen?
Könnte gehen: Wenn RTC=="dead", stelle WLAN-Verbindung her, hole Zeit und berechne daraus die Länge der nächsten Tiefschlafphase. Also müsste nur der RTC.getTime() in eine entsprechende Funktion ausgelagert werden. Und das Programm müsste damit umgehen können, dass so ein getTime() auch mal länger als ein paar Millisekunden dauern kann.

Das ESP8266-Modul muss auf der ATmega328-Seite nicht gesondert überwacht werden. Wenn das nicht mehr tut, sieht man das ja im Log bzw. der Ausgabe des Python-Scripts. Also die fehlenden Meldungen.


die Pegelwandler sind da

Heute kamen (verspätet) zwei Stück "Adafruit bi-direktionaler Pegelwandler, 4 Kanal".
Aufgebaut sieht die Schaltung damit so aus:
ESP8266 mit Pegelwandler

Die Tabelle zur erneut durchgeführten Testreihe ist nicht wirklich besser geworden.
Vcc ESP8266
Antwort auf ping
Reaktion auf AT-Befehle
Datenaustausch mit dem Python-Script
2.2V
nein
keine Reaktion nicht möglich
2.4V
nein
keine Reaktion nicht möglich
2.5V
nein
keine Reaktion nicht möglich
2.7V
nein
keine Reaktion nicht möglich
2.8V
ja (wenige DUP's) korrekt
stabil
3.0V
ja
korrekt stabil
3.3V
ja
korrekt stabil
3.5V
ja
korrekt stabil
3.7V
ja
korrekt
stabil
3.9V
ja
korrekt
stabil
4.0V
ja (viele DUP's) korrekt
stabil

Das hatte ich mir anders erhofft. An den Pegeln bzw. der Kommunikation zwischen ESP8266 und ArduinoUNO lag es also offenbar nicht.

Aber es wird immer offensichtlicher, dass der ESP8266 durch Änderungen an seiner Versorgungsspannung aus dem Tritt kommt.
Jedes mal dauerte seine Zeit, bis er sich nach einer Spannungs-Änderung wieder gefangen hatte.

Fazit: es hilft nix! Das Teil braucht stabile bzw. stabilisierte 3.3V. Sonst macht das keinen Spaß.

Morgen sollten zwei Typen von 3.3V-Festspannungsreglern kommen.
Allerdings werde ich mit deren "Minimum Load" wieder deutlich in den einstelligen Milli-Ampere-Bereich rutschen. :-(
Vielleicht kann ich sie ja irgendwie "abschalten", wenn ich (zu 99.9% der Zeit) die 3.3V für den ESP8266 nicht brauche.

Dann hätte die RTC aber auch kein VBAT mehr....
...hmm...tja...die RTC funktioniert auch ohne 3.3V an VBAT....zumal sie nach jedem Restart bzw. Stromausfall in setup() ohnehin neu gestellt wird.
Der einzige Nachteil einer fehlenden RTC-Batterie würde sich nur dann einstellen, wenn nach Stromausfall keine Verbindung zum Python-Script hergestellt werden kann. Das müsste im Programm abgefangen werden.
Vielleicht dadurch, dass immer das letzte via WLAN empfangene Datum ins EEPROM geschrieben wird.
In setup() könnte dann geprüft werden, ob die RTC ein Datum liefert, das zeitlich ein paar Tage nach dem EEPROM-Datum liegt.
Oder einfacher: ohne Backup-Batterie liefert die RTC nach dem Einschalten "2000.01.01 00:00:00".
Wird von der RTC ein Datum vor dem "2015.10.29 00:00:00" geliefert, kann was nicht stimmen und setup() dürfte erst verlassen werden, wenn Datum+Zeit vom Python-Script empfangen wurde.

Obwohl...ist doch alles Quatsch. Die RTC wird ja nur bei Bedarf über einen Pin des ATmega328 mit 5V versorgt.
Also brauche ich die 3.3V zwingend. Das ginge notfalls auch mit einer dieser fiesen, hochgiftigen Knopfzellen.
Oder besser einem SuperCap!?  Mal gucken, ob die allwissende Müllhalde was dazu hat.
...hat sie - offenbar bin ich nicht der erste, der auf diese Idee gekommen ist ;-) ... und die Knopfzelle ist bei dem DS1307 wohl die bessere Idee.

Trotzdem bleibt der zu prüfende Umstand bestehen, dass die RTC ein nicht plausibles Datum liefern könnte.
Sie könnte über WLAN korrigiert werden ... wenn der RTC dann aber die 5V-Stromversorgung über den Pin des ATmega328 genommen wird, ist sie wieder tot. Also blöde Idee.
Es hilft aber nix. Wenn die RTC keine Zeit liefert und die Zeit nicht über WLAN geholt werden kann, ist die Ansteuerung der Rollläden zu sperren.

Statt einer Knopfzelle sollte es auch ein Spannungsteiler aus zwei Widerständen tun.
Sobald die Gesamtschaltung an 5V hängt, wäre damit die RTC versorgt, würde aber nur den VBAT-Strom von <1µA ziehen.
Laut Datenblatt zieht die DS1307 an VBAT typisch 300nA bei VCC=0V und VBAT=3V.
Keine Ahnung, ob man das jetzt so rechnen kann, aber gemäß R=U/I wäre das ein Eingangswiderstand von 3V/300nA=10MΩ.
Wenn ich VBAT nun über einen 5,6MΩ Widerstand an +5V lege, sollten darüber 5,6MΩ*300nA=1,68V abfallen.
Wären dann zwar 3,32V an VBAT, aber das wäre unter den erlaubten 3,5V. Könnte klappen.
Oder auch nicht. Also doch die schmutzige Knopfzelle.

Nachtrag: Eben habe ich in einem DM-Drogeriemarkt nach Knopfzellen geschaut. Die hatten so sieben oder acht unterschiedliche Modelle. Alle vom Hersteller "Energizer" und alle oberhalb von 3€.
Die Abmessungen und eine Spannung war jeweils angegeben. Aber sonst nix. Das ist ja vielleicht bizarr.
Bei einer Batterie ist doch wohl die Kapazität ein nicht ganz unwesentliches Kriterium.
Ich habe dann eine Energizer 2016 quasi blind gekauft.
Auf der Hersteller-Seite auch nix zur Kapazität. Bei idealo.de habe ich schließlich was gefunden. Mit etwas Glück stimmt das....und die Knopfzelle hat 90mAh...was laut Datenblatt des DS1307 für mehr als zehn Jahre langen sollte.


3.3V Festspannungsregler

Gestern sind die Festspannungsregler geliefert worden.
Ich habe zwei Typen bestellt:
Typ
Preis/Stück (Okt. 2015)
LT 1086 CT3,3 3,99 €
LM 1117 T3,3 1,15 €

Der LT1086CT3.3 zieht ohne Last 4.3mA (laut Datenblatt typisch 5mA), der LM1117T3.3 zieht 5.7mA (laut Datenblatt typisch 10mA).
Beide sollten genug Strom für den ESP8266 liefern (1.5A bzw. 800mA) können.

Der LT zieht mit angeschlossenem (unveränderten) ESP8266 (PD=LOW) 5.4mA und bei einem ESP8266, dessen rote LED abgelötet wurde, 5.1mA.
Der LM zieht 6.3mA bei dem ESP8266 mit roter LED, 6mA ohne rote LED.

Meine Versuche, den Regler mit einem vorgeschalteten NPN-Transistor (Emitter an VIN, Kollektor an +5V) abzuschalten, waren nur so lange erfolgreich, wie das Voltmeter alleine angeschlossen war. Mit Last (ESP8266) ist die Spannung eingebrochen und bei unter 2.8V gelandet - was der ESP8266 bekanntermaßen nicht mag.
Vielleicht wäre ein MOSFET besser geeignet. Einige N-Kanal MOSFETs hätte ich ja rumliegen, hier würde aber wohl eine P-Kanal-Version benötigt werden.

Und weil ich jetzt bzgl. "Teile nachbestellen...und darauf warten" die Faxen dicke habe, werde ich ganz stumpf ein 5V-Relais nehmen, um damit den Festspannungsregler und den ESP8266 abzuschalten. Das ist zwar total unelegant, aber egal.
Neulich habe ich mir beim Auffüllen meiner Amazon-Einkaufsliste fünf Relais (SRD-05VDC-SL-C) für zusammen 3,02€ bestellt. Das Päckchen aus China ist bereits eingetroffen und daher wird es nun eins von denen.
Das zieht laut Specs zwar 90mA, jedoch lieber 30 Sekunden pro Tag mal zusätzliche 90mA, statt konstant 5mA.
Als Schaltplan sähe das dann etwa so aus:
Schaltplan Stromversorgung ESP8266

Und als Bonus-Feature gibts bei dieser Version der Schaltung dann sogar ein dezentes, akustisches Feedback beim Ein- und Ausschalten der WLAN-Kommunikation. :-)

Weil die Versorgungsspannung jetzt stabilisiert auf 3.3V läuft, wird das Pegelwandler-Modul zwischen ESP8266 und ATmega328 nicht mehr benötigt bzw. können stattdessen zwei Spannungsteiler aus Widerständen (vor den RX- und PD-Pins des ESP8266) verwendet werden.

Damit sollte nun alles beisammen sein, um den ganzen Kram mal zusammen auf einer Rochraster-Platte zusammenzuschalten.


Schritt 7 - die finale Schaltung

Jetzt sind die ersten Bauteile auf der Rochraster-Platte verbaut.
Noch siehts so aus:
Foto von allen Komponenten

Und klappt :-)
Sogar mit einem Akku-Pack aus vier Mignon-Zellen (Eneloop, 1.900mAh).

Bevor alles auf der Rochraster-Platte verbaut wird, wollte ich noch die oben angedachte Spannungs-Überwachung einbauen.
Weil die eigene Versorgungsspannung gemessen werden soll, kann es wohl nicht schaden, analogReference(INTERNAL) zu verwenden.
Damit können dann maximal 1.1V gemessen werden. Also brauchts einen Spannungsteiler auf 1 / 5.
Und weil wenig Strom verbraucht werden soll, habe ich Widerstände mit 4MΩ und 1MΩ verwendet.
Darüber würde konstant 1µA fließen.
Leider passten die vom ArduinoUNO gemessenen Werte nicht so ganz zu den erwarteten Werten.
Hätte 5V sein sollen, es wurden aber nur 4.55V gemeldet.
Daraufhin habe ich mal mit einem Multimeter gemessen.
Über beiden Widerständen hatte ich 5.06V.
Über dem 1MΩ hatte ich 0.48V, über dem 4MΩ waren es 2.28V.
Uppps...wie geht das denn?
Dann erstmal den A0-Pin des ArduinoUNO gezogen...brachte keine Änderung.
Hat ein paar Sekunden gedauert, bis mir dann klar wurde, dass das an dem Innenwiderstand des Multimeters liegen muss.
Und das wird dann wohl auch das Problem des ArduinoUNO-Analog-Pins sein.

Wenn ich nun aber niederohmiger werden muss, verbrate ich konstant unverhältnismäßig viel Strom.
Und das nur, um 2x am Tag die Spannung messen zu können. Das geht nicht. Das muss abschaltbar sein.
Glücklicherweise habe ich ja schon was abschaltbares - nämlich den 3.3V-Regler.
Und an dessen VIN liegen abschaltbare 5V an. Perfekt :-)

Also werden zwei Widerstände im Kilo-Ohm-Bereich vor den 3.3V-Regler zwischen +5V und GND geschaltet.
Natürlich habe ich keinen Widerstand mit 4KΩ. Nur 4.7KΩ. Aber 8KΩ und 2KΩ (1KΩ+1KΩ) ergibt auch das gewünschte Verhältnis.
Das Multimeter misst über der Reihenschaltung der beiden 1KΩ-Widerstände 0.995V, ein zweites Multimeter 0,987V.
Der ArduinoUNO aber 5.15V.
Laut Multimeter sollte er 0.995V*5=4,975V messen - was einer Abweichung von 0,175V entspricht.

Messe ich stattdessen die Spannung aus dem Labornetzteil, habe ich im Bereich zwischen 4.0V und 5.5V eine relativ konstante Abweichung von 0.1V.
Damit kann man leben. Hoffentlich ändert sich das nicht wieder in dem Moment, wo die gemessene Spannung die eigene Versorgungsspannung ist.

Der finale Schaltplan sieht damit folgendermaßen aus:
finaler Schaltplan

Doch nicht final. :-(

Wenn das Relais eingeschaltet wird und damit auch der 3.3V-Regler plötzlich Strom zieht, macht der ATmega328 einen Reset.
Das hat zur Folge, dass das Relais rattert...und natürlich nix funktioniert.
Auch der Austausch des billigen China-Relais gegen ein Pin-gleiches von Finder hat keine Änderung gebracht.
Etwas besser sah es nach Einbau eines 2200µF Elkos aus.
Damit konnte ich das Finder-Relais immerhin ohne den 3.3V-Regler vom ATmega328 an- und ausschalten - nur leider nützt mir das rein gar nichts.

Nach Entfernen des Relais und Dauerbetrieb des 3.3V-Regler funktioniert alles bestens.
Die Schaltung zieht dabei konstante 6.4mA (mit einem ESP8266 ohne roter LED) im Schlafmodus, 140mA bei WLAN-Kommunikation.

Foto der Schaltung

Für Akku-Betrieb ist das jetzt unbrauchbar.
Wenn die Akkus 1.900mAh haben und ich konstant 6.5mA ziehe, reicht eine Akku-Ladung gerade mal für 292 Stunden bzw. 12 Tage.
Selbst die Mono-Zellen mit ihren 10.000mAh würden es nur auf zwei Monate bringen.
So kann das nicht bleiben.
Aber ich kann immerhin schon mal den Dauerbetrieb üben, ohne meinen Rechner durchlaufen lassen zu müssen.

Die Programme sehen mittlerweile so aus:
    SocketServerESP8266v3.py
    ESP8266-AnsteuerungV5.ino

...to be continued.


der erste Arbeitstag

Also Arbeitstag ist eigentlich falsch. Zwischen 21:00 Uhr und 08:00 Uhr hat die Schaltung konstant ihre Kreise gedreht:
2015.10.31 21:00:21 Verbindung geschlossen ESP8266-1 (192.168.42.101:13374)

2015.11.01 03:00:05 Verbindung hergestellt ESP8266-1 (192.168.42.101:37414)
2015.11.01 03:00:06 Autorisierung abgeschlossen
2015.11.01 03:00:06 Abweichung [sek]: 0
2015.11.01 03:00:06 Datum/Zeit übermittelt
2015.11.01 03:00:07 Zeiten-Tabelle ist aktuell
2015.11.01 03:00:07 Akku-Spannung: 0.94V
2015.11.01 03:00:07 Firmware-Version: 1.5.1
2015.11.01 03:00:07 Verbindung geschlossen ESP8266-1 (192.168.42.101:37414)

2015.11.01 06:30:08 Verbindung hergestellt ESP8266-1 (192.168.42.101:12751)
2015.11.01 06:30:08 Info: Rollladen hoch
2015.11.01 06:30:08 Verbindung geschlossen ESP8266-1 (192.168.42.101:12751)


Schaltung und Programm haben die Nacht also offenbar gut überstanden.
Praktischerweise trat dabei ein Monatswechsel auf, der korrekt verarbeitet wurde (sonst wäre die "Rollladen hoch"-Meldung bereits um 06:00 gekommen).

Nach Erweiterung der Schaltung um den Spannungsteiler zur Messung der eigenen Spannungsversorgung (hatte ich vorher vergessen), kamen erstmal sinnlose Werte heraus. Nach einigen fruchtlosen Test habe ich mir nochmal die Doku zu analogReference() zu Gemüte geführt. Wegen des unter "Warning" stehenden Textes habe ich die Verbindung von AREF (Pin 21 am ATmega328) mit VCC und AVCC entfernt. AREF hängt somit jetzt in der Luft.
Und nun klappt es. Auffällig war dabei, dass offenbar jeder ATmega328-Chip seinen eigenen Korrekturwert benötigt. Mal ist es -0.1V, mal passt es und mal brauchts +0.1V.
Die Schaltung sendet noch erfolgreich bei 4.4V Eingangsspannung. Das liegt zwar schon am untersten Limit des für den LM1117T3.3 möglichen Deltas zwischen Eingangs- und Ausgangsspannung, trotzdem liefert er stoisch seine 3.3V.
Wenn man den ESP8266 zu sehr mit nicht ausreichenden Spannungen quält, vergisst der schon mal seine Default-Einstellung oder kommt total durcheinander. Ein Exemplar musste ich via USB-zu-TTL-Modul und CuteCom erstmal mit AT+RESTORE ins Leben zurückholen .... bevor er wieder Konfigurationen annehmen wollte.

Weiterhin ist mir bei den Tests aufgefallen, dass das Python-Script mit einem  nmap localhost -p 2626  nicht klarkommt. Da brauchts noch zwei weitere try-except-Blöcke, um resistent gegen solche bösen Hacker-Tools zu werden.


Alles sehr schön soweit.
Nur ist das Hauptproblem ja gerade ausgeblendet: Das Ding zieht sinnloserweise konstant Strom im Milli-Ampere-Bereich.

Das Relais konnte ich ja schon ohne Reset des ATmega328 ein- und ausschalten.
Erst wenn der 3.3V-Regler über den Schließer des Relais mit Strom versorgt wurde, kam der ATmega328 aus dem Tritt.
Der ESP8266 war dabei nicht gesteckt. An dessen Einschalt-Strom lag es also nicht. Leider. Denn den könnte ich im Programm ja problemlos zeitverzögert aktivieren.

Ein möglicher Test wäre, die Schaltung jetzt so umzufrickeln, dass ich den 3.3V-Regler per Schalter oder Drahtbrücke im laufenden Betrieb des ATmega328 an- und ausschalten könnte. Der ATmega328 würde mit einem Blink-Programm (z.B. mit jew. drei Sekunden Pause zwischen AN-AUS-AN) laufen. Wenn die Blink-Frequenz durcheinander käme, wüsste ich, dass der ATmega328 gerade einen Reset gemacht hat.
Aber was würde mir diese Erkenntnis bringen...?
Wenn der ATmega328 keinen Reset macht, ist das zwar schön, nützt mir als Erkenntnis aber nicht viel.
Und wenn er einen Reset macht, weiß ich auch nur, dass ich den 3.3V-Regler tauschen muss.
Da kann ich mich auch gleich nach einem neuen 3.3V-Regler umsehen.
Vorzugsweise einer, mit einem Enable-Pin.
Dadurch könnte ich das Relais einsparen. Hätte dann allerdings keine "abschaltbaren 5V" mehr für den Spannungsteiler zum Messen der eigenen Versorgungsspannung.
Also doch erstmal den Test machen.
Und siehe da: das Einschalten des Reglers reicht für den Reset.
Eine Nachfrage im mikrocontroller.net-Forum hat mich dann auf die Idee gebracht, die beiden Tantal-Elkos vor und hinter dem Regler auszubauen.
Und schon gehts.
Auch mit dem Relais.
Weil der erste zeitgesteuerte Connect nach PowerOn oft nicht so wollte, wie ich wollte, habe ich noch je einen 100nF Keramik-Kondensator zwischen Vcc und GND in unmittelbarer Nähe zu ATmega328 und ESP8266 gelegt.

Meine Selbst-Quälerein zum Stromverbrauch haben sich ausgezahlt.
Wenn der ATmega328 schläft, zieht die Schaltung 13µA.
Wenn er aufwacht und die Lebenszeichen-LED blinken lässt, irgendwas um die 20mA (mein Multimeter kann so kurze Impulse nicht vernünftig messen).
Und bei WLAN-Traffic um die 210mA (also Relais + ESP8266).

Jetzt bin ich glücklich :-)

Aber warum zieht sie im Tiefschlaf eigentlich 13µA ... statt der 0.35µA von oben ?
Egal. War nur Spaß.

Bei 13µA sollten Akkus mit 1900mAh über 16 Jahre reichen, wenn man mal von sowas wie Selbstentladung absieht....
Bei 20mA für 95 Stunden.
Und bei 210mA für 9 Stunden.

Nach 8 Sekunden braucht die Schaltung für 0.05 Sekunden 20mA.
Also pro Tag 24*60*60 / 8.05 = 10733 mal 0,05 = 536,65 Sekunden bzw. 9 Minuten lang.
Bei 1900mAh und 9 Minuten pro Tag mit 20mA:
    1900 [Milli-Ampere / Stunde] / 20 [Milli-Ampere] * 60 [Minuten/Stunde] / 9 [Minuten/Tag]=633 Tage.

Dann kommt noch zweimal am Tag ca. 30 Sekunden WLAN-Kommunikation und zweimal am Tag ca. 2 Sekunden IR-LED-Ansteuerung hinzu.

Somit sollten die Akkus durchaus ein Jahr durchhalten.

Mit der neusten Version des Schaltplanes und der Programme warte ich noch, bis die Sende-Einheit gebaut ist und funktioniert.


Und noch eine kleine nachträgliche Rechnung ... speziell für den zweifelnden Marcel H aus B:
Ein kurzes IR-Bit ist 600µs lang, ein langes 3700µs.
Bei angenommener Gleichverteilung von kurz/lang bzw. 0/1 also (3700µs+600µs)/2=2150µs pro Bit.
Nun kommt da noch die Träger-Frequenz von 40KHz drauf (oder auch drunter). Und die hat 50% duty cycle.
Also nochmal durch zwei. Macht 1075µs lang Stromverbrauch pro Bit.
Jedes zweite Bit ist ein Dunkel-Bit. Nochmal durch zwei. Macht 538µs.
Ein Signal hat 70 Bit, zwei Signale werden pro Ansteuerung gesendet. Zwei Ansteuerungen pro Tag.
Macht zusammen 70*2*2*538µs=150640µs=151ms pro Tag, an denen durch beide IR-LEDs je ca. 0.9A fließen.
Okay, meine 2 Sekunden waren etwas hoch gegriffen. Aber geht weiter:
2*0.9A=1.8A oder zur Vereinfachung 1.9A...weil es dann besser zu den 1900mAh der Akkus passt.
Akkus mit 1900mAh können die IR-LEDs mit 1900mA so rein rechnerisch also genau eine Stunde lang leuchten lassen.
Da ich aber pro Tag nur 151ms lang aktiv sende, reichen die Akkus für 1[h]*60[m/h]*60[s/m]*1000[ms/s]/151[ms/t]=23841[t] Tage oder auch 65 Jahre.
So Meister....und nun kommst du....was wird mir da wohl die Akkus leerlutschen?  ;-)

Abgesehen davon, dass wir beiden alten Säcke uns dann schon lange die Radieschen von unten angucken, ist in 65 Jahren eh längst Zombi-Apokalypse.
Und da bleiben die Rollläden natürlich lieber dauerhaft unten.


Schritt 8 - letzter Schliff vor dem Einsatz

Was ich noch bauen muss, ist die Sende-Einheit. Oder auch zwei davon. Und ein Gehäuse.

Es könnte von Vorteil sein, noch einen Schalter einzubauen, durch den das Programm in einen IR-LED-Justage-Modus gebracht werden kann.
Also zum Ausrichten der IR-LED. Nur mal so als Merker. Erstmal abwarten, wie empfindlich die Sende-Einheit auf ihre Ausrichtung reagiert.


Bei den ersten Tests wurden die Rollläden nicht angesteuert.
Weitere Tests mit einem ArduinoUNO, TSOP31240 und getRollotronIRcode.ino aus Schritt 1 haben gezeigt, dass nur Quatsch gesendet wurde.
Selbst ein Test mit send40KHz-v2.1.ino aus Schritt 2.2 hat nicht [mehr] funktioniert.
Erst mit send40KHz-v2.ino klappte wieder alles.
Und mit send40KHz-v2.1.ino schließlich auch, nachdem ich die zwei PROGMEM-Direktiven rausgenommen hatte.
Sehr sonderbar. Sollte ich die v2.1 nicht mehr mit der Sende-Einheit getestet haben? Eigentlich unwahrscheinlich ... aber denkbar.
Und egal ... es macht nix aus, weil noch genug RAM da ist (zumindest bei abgeschaltetem Debug-Code).

Der nächste echte Test (im Wohnzimmer) hat die vier Schwenkwickler 1 - 4 runtergefahren (die nebeneinander an einer Wand befestigt sind).
Gut...ich habe jetzt einen Vorwiderstand für die IR-LED mit 5Ω statt 4Ω, aber das sollte nicht soviel ausmachen.
Demnach muss wohl eine zweite IR-LED hinzukommen.
Zur Ausrichtbarkeit werden beide IR-LEDs oben aus dem Gehäuse rausgeführt, an je einem Winkel montiert und beide Winkel drehbar auf dem Gehäuse befestigt. Also zur Abwechslung mal eine kleine Laubsäge-Arbeit.
Oder ich finde noch irgendwas Fertiges in der Größe einer Streichholz-Schachtel. Vorzugsweise schwarz und aus Plastik.
Ideal wäre es, wenn sich die Frontplatte, an der die IR-LED verschraubt ist, ein wenig nach vorne kippen ließe.
Etwa so:
              |                                     LE  /
           LED#   oder (etwas übertrieben)            D#
         _    |                                 _     /
    =====+====+                            =====
+====+
         T                                      T
Alternativ (und einfacher) könnte man die ganze LED-Einheit auf der Rückseite leicht anheben.
Ich habe meinem Jüngsten gerade zwei Packungen TicTac spendiert.
Er musste sich dafür nur auf sein Fahrrad schwingen, sie einkaufen, den Inhalt in eine kleine Schüssel umfüllen und mir die Packungen geben.

Aber warum überhaupt gekippt werden soll: die Schaltung soll auf einem Schrank in 2m Höhe stehen, die IR-Empfänger der Schwenkwickler befinden sich hingegen in 1.3m bzw. 1.15m Höhe.
Konkret wird es so aussehen:
Skizze Wohnzimmer

Für die vier nebeneinander angebrachten Schwenkwickler ergibt sich ein Winkel von knapp 30°. Für alle fünf etwa 60°.
Die CQY-99 hat laut Datenblatt einen Abstrahlwinkel von 44°, die LD-274 von 20°.
Somit wirds eindeutig: es braucht zwei IR-LEDs, aber das Kippen der LED-Einheiten ist nicht ganz so wichtig.

Wahrscheinlich könnte es nicht schaden, für den einzelnen Schwenkwickler (Nummer 5) eine LD-274-3 einzusetzen.
Die 44° der CQY-99 würden den Schwenkwickler Nummer 4 ohnehin nicht erreichen, wenn mittig auf Nummer 5 ausgerichtet würde.
Weil ich die Datenblätter der beiden IR-LEDs so deute, dass die Dinger unterschiedliche Schaltzeiten haben, ist es wohl auch besser, wenn jede IR-LED ihren eigenen Bereich abdeckt - und das Signal der anderen nicht [evt. leicht asynchron] überlagert.

Ein gewisses Problem besteht darin, dass die Schwenkwickler sehr wahrscheinlich deswegen Schwenkwickler heißen, weil sie sich schwenken lassen.
Und zwar um bis zu 90°. Die Wickler 1 bis 3 sind nach links schwenkbar, der Wickler 4 nach rechts und Wickler 5 wieder nach links (bzw. in der Zeichnung nach oben).
Im jeweils maximal geschwenkten Zustand würde ich mit viel Glück die Wickler 1 bis 3 per Infrarot erreichen. Die anderen beiden bestenfalls über Bande. Wände und Decke sind weiß gestrichen ... aber ich glaube nicht, dass davon genug reflektiert wird.
Folglich müsste den Schwenkwicklern für sicheren Betrieb über meine Schaltung irgendwie ihre Schwenkbarkeit genommen werden. Die brauche ich nämlich eh nicht. Mit einem Stück beidseitigem Klebeband könnte man sie an die Tapete kleben. Aber dann kommt garantiert drei Tage später ein ungeschickter Vollhonk des Weges, reißt brutal an einem der vermeintlich schwenkbaren Schwenkwickler und ramponiert die Tapete. Daher lieber ein entsprechend bemessener "Z-Verbinder", der über dem Schwenkwickler an die Wand geschraubt wird, mit seinem anderen Winkel über den Schwenkwickler ragt und diesen somit am geschwenkt werden hindert. Es müsste allerdings ein hübsches Modell sein - sonst gibts Mecker von der Gattin.

Aber selbst wenn das Problem mit der Schwenkbarkeit gelöst wäre, bliebe immer noch das Problem von temporär im Weg stehenden Dingen (Pflanzen, Gardinen, Noten-Ständer ... oder auch Personen), durch die die Sicht-Verbindung zwischen IR-Sender und IR-Empfänger gestört würde. Das ließe sich dadurch etwas abschwächen, dass die Schwenkwickler möglichst weit oben angebracht wären .... unter der Annahme, dass sich in 1.8 Meter Höhe üblicherweise weniger Dinge befinden, als in 1.2 Meter Höhe.
Deswegen werde ich die Schwenkwickler jetzt aber nicht neu montieren.... Bleibt halt ein Restrisiko.
Höchstens gegen sich bewegende Dinge (also eher Personen) könnte die Software vorsehen, ihr Kommando an die Rollläden mehrmals zu senden. Die Pause zwischen den erneut gesendeten Kommandos müsste mindestens so lang sein, wie die Rollläden für das komplette Rauf- oder Runterfahren benötigen. Ansonsten würde die Wiederholung des Kommandos von den Schwenkwicklern als Stop-Befehl für den vorigen Befehl interpretiert werden.

Mit einem (Winkel-technisch geschickt gewählten) zweiten Standort für eine weitere IR-LED ließe sich die Gefahr von im Weg stehenden Dingen noch besser abfangen. Der Transistor hätte genug Leistung für eine dritte IR-LED, nur leider habe ich auf der Lochraster-Platte keinen Platz mehr.

Die sicherste Variante würde eine IR-LED pro Schwenkwickler benötigen, die am Schwenkwickler selbst, unmittelbar vor dessen IR-Empfänger, montiert wäre. Das hatte ich durchaus schon am Anfang des Projektes überlegt ... und hätte es vielleicht auch so gemacht, wenn es nicht den Schwenkwickler mit der Nummer 5 gäbe: das Kabel zwischen Nummer 4 und Nummer 5 wäre mindestens acht Meter lang und müsste unterhalb der Terrassen-Tür verlegt werden.

Erstmal bleibt alles, wie ursprünglich geplant.
Für den ersten Test mit zwei IR-LEDs sah der Aufbau noch folgendermaßen aus:
Schaltung ohne Gehäuse

Die IR-LED (LM-274-3) links-unten ist auf den Wickler Nummer 5 ausgerichtet, die andere IR-LED (CQY-99) auf die Wickler 1 bis 4.
Die ganzen Drahtbrücken auf dem Steckbrett dienen nur der Ausrichtung und Fixierung der IR-LEDs und, um die Einheit transportabel zu machen.

Was soll ich sagen....funktioniert :-)
Alle fünf Schwenkwickler werden erreicht.
Mal sehen, wie lange das jetzt in dieser Aufbaustufe installiert bleibt.
Schließlich stört das da oben auf dem Schrank niemanden. Also mich zumindest nicht ;-)
Schon damals in der Ausbildung habe ich durchaus gerne Prüfgeräte gebaut. Auf den Anteil Gehäuse-Bearbeitung hatte ich dabei immer am wenigsten Lust.

Nun noch Schaltplan und Programme in neuster Version:
SchaltplanV6

Für das Programm ESP8266-AnsteuerungV6.ino wird jetzt bzgl. Speicherplatz-Belegung gemeldet:
Das Python-Script SocketServerESP8266v6.py hat zwecks Vereinheitlichung einen Versions-Sprung von 3 auf 6 gemacht.


Schritt 9 - Gehäuse-Bau und Dauerbetrieb

Der Gehäuse-Bau war schmerzlos. Es hat insgesamt weniger als drei Stunden gedauert.
Die zwei TicTac-Dosen passen gut und sind schön schwergängig drehbar.
Der Akku-Pack mit vier Mignon-Akkus ist ebenfalls außen auf dem Gehäuse gelandet. Spart das Öffnen des Gehäuses zum Wechsel.
Vorne die Lebenszeichen-LED, hinten der Reset-Taster.
Das einzige, was mir nicht 100% gefällt, ist die Höhe des Gehäuses. Da ist jetzt reichlich Luft drin.
Aber das war das einzige aus meinem Fundus der vor Jahren "auf Vorrat" gekauften Gehäuse, in das die Lochraster-Platte reinpasste.
Beim nächsten Projekt sollte ich das Gehäuse frühzeitiger auswählen, um dann die noch unbestückte Leiterplatte ggf. ein paar Millimeter verkleinern zu können.

das Gehäuse am Zielort    das Gehäuse von oben

Jetzt hat die Schaltung seit drei Tagen ihren Dienst an den Rollläden zuverlässig erfüllt.
Lediglich ein kleines Detail stört mich noch: ich habe extra Aufwand betrieben, damit sich die Schaltung zweimal am Tag auf die Sekunde genau beim Python-Script meldet - und dafür sogar mindestens doppelten Akku-Verbrauch für die WLAN-Phase in Kauf genommen.
Trotzdem zeigt mir das Python-Script, dass die Connects immer pünktlich um 03:00:03 bzw. 15:00:03 erfolgen. Ich will die aber nicht drei Sekunden nach der eingestellten Zeit haben....
Bei erneuten Tests (mit einem anderen ESP8266-Modul an einem ArduinoUNO) habe ich mich gefragt, warum eigentlich immer erst der dritte Connect-Versuch erfolgreich ist. Dann habe ich aus Spaß einfach mal den Hostnamen durch die entsprechende IP-Adresse ersetzt. Siehe da...nun ist bereits der erste Connect-Versuch erfolgreich. Da ist wohl die Befragung des DNS etwas langsam...oder was auch immer!?
Folgende Werte für die Dauer des jeweiligen Funktionsaufrufs waren auf ±10 Millisekunden stabil:
Funktion
Hostname
numerische IP-Adresse
ESP8266_power_on_setup_and_wait_for_ip() 3779 ms 3787 ms
ESP8266_connect_TCPIP() 2671 ms
102 ms
Der einzige Unterschied war:
    ESP8266.println(F("AT+CIPSTART=\"TCP\",\"gw\",2626"));
bzw.:
    ESP8266.println(F("AT+CIPSTART=\"TCP\",\"192.168.42.254\",2626"));

Weiterhin war in ESP8266_wait_for_ip() noch ein kleiner Denkfehler: wenn der ESP8266 bereits über AT-Befehle ansprechbar ist, wird der nach ESP8266.println(F("AT+CIPSTATUS")) folgende ESP8266.find("\r\nSTATUS:") nie in den Timeout von einer Sekunde laufen. Somit muss in ESP8266_wait_for_ip() aktiv gewartet werden, um den als Parameter übergebenen timeout_seconds einzuhalten.
Nebenbei habe ich die Zeit für die Lebenszeichen-LED von 50ms auf 10ms runtergesetzt. Das ist immer noch erkennbar und spart Strom.


Heute (08.11.2015) habe ich den Socket-Server insofern umgebaut, dass er jetzt von einem Überwachungs-Script abgefragt werden kann.
Gestartet wird er mit:
    screen -S SocketServerESP8266v7 -d -m /home/dede/SocketServerESP8266v7.py

Im Log der Version 7 werden Warnungen jetzt farblich hervorgehoben:
Log-Ansicht

Noch läuft das Überwachungs-Script im Terminal bzw. Text-Modus und liefert z.B.:
ip_address                 =192.168.42.101
hostname                   =ESP8266-1
current_authorization_state=WAIT
was_authorized             =True
current_socket             =None
last_seen_here             =None
last_connect_local_time    =2015.11.08 19:30:18
last_time_delta_seconds    =0
last_voltage               =5.12
last_firmware_version      =1.6.1
last_restart_timestamp     =
2015.11.08 19:30:18
last_warning_code          =1


Final möchte ich ein kleines Fensterchen auf dem Desktop, in dem per wxWidgets (oder zur Abwechslung vielleicht auch mal per Gtk) die relevanten Daten angezeigt werden.
Also sowas wie:
Wenn das Überwachungs-Script mit GUI läuft, werde ich nochmal alle Programme in neuster Version hier verlinken.



Nun ist der RollladenMonitor auch fertig.
Sieht so aus:
Screenshot RollladenMonitor     oder mit maximalen Warnungen    Screenshot RollladenMonitor mit allen Warnungen

Der linke Screenshot ist die normale Ansicht und zeigt die Attribute der Schaltung von der letzten Kontakt-Aufnahme.
Beim rechten Screenshot kommuniziert der RollladenMonitor mit einem SocketServer, der seine Daten statt von einer Schaltung von einem Test-Script bekommen hat. Darüber wurde der maximale Fehler-Status eingestellt. Also "letzte Verbindung" älter als 12 Stunden, Akku-Spannung unterhalb von 4.4V, mehr als drei Sekunden Abweichung der RTC und alle Warnungen eingeschaltet, außer der Restart-Warnung.
Die Warnungen werden im Fenster (aus Platz-Gründen) als Zahl dargestellt. Wird der Mauszeiger auf die Zahl bewegt, erscheint ein Popup mit der Bedeutung der in der Zahl gesetzten Bits.

Hier noch die Programme in aktueller Version:
ESP8266-AnsteuerungV7.ino
SocketServerESP8266v7.py
RollladenMonitor.py

Die Attribute "letzter Restart" und "letzte Verbindung" im RollladenMonitor sind in Ortszeit angegeben.
Somit wird die "letzte Verbindung" einmal pro Jahr eine Stunde lang anzeigen, dass die 12 Stunden zwischen den Meldungen überschritten wurden.
Am letzten Sonntag im März wird um 02:00 Uhr Normalzeit auf 03:00 Uhr Ortszeit vorgestellt. Die Nacht ist eine Stunde kürzer.
Damit ist es für den RollladenMonitor um 02:00 Uhr Normalzeit bereits 03:00 Uhr Ortszeit, die Schaltung wird sich aber erst um 03:00 Uhr Normalzeit, also 04:00 Uhr Ortszeit melden. Folglich gibt es jeweils am letzten Sonntag im März zwischen 03:00 Uhr und 04:00 Uhr Ortszeit eine Fehlermarkierung. Da mich das zu dieser nachtschlafenden Zeit nicht stört, bleibt das so.

Nach der oben erwähnten Änderung des Connects von Hostname nach IP-Adresse kamen die Connect-Meldungen gerne mal um 02:59:59 bzw. 14:59:59. Was ich eigentlich nicht ganz verstehe. Die Abweichung der Uhren wurde mit 0 Sekunden gemeldet, trotzdem kam der Connect etwas zu früh. Außerdem schicke ich die aktuelle Uhrzeit mit abgeschnittener Millisekunden-Information zur Schaltung und berücksichtige weder die Zeit für die Übertragung noch für die Verarbeitung. Da mein Programm erst aktiv wird, wenn die eingestellte Zeit laut RTC erreicht wurde, kann m.E. nur noch die RTC innerhalb von 12 Stunden soviel vorgehen, dass sowohl die Übertragungszeit als auch die Verarbeitungsdauer kompensiert wird und dann noch ein paar Millisekunden dazu kommen. Um der Sache auf die Schliche zu kommen, könnte ich die Log-Anzeige im SocketServer temporär so umbauen, dass auch die Millisekunden-Information angezeigt wird. Aber eigentlich ist das auch nicht so wichtig. Daher habe ich den Vorlauf zum Anglühen des ESP8266 wieder ausgebaut und fange erst zum eingestellten Zeitpunkt damit an, den ESP8266 mit Strom zu versorgen. Damit kommt der Connect dann garantiert bzw. mindestens drei Sekunden zu spät, aber ich spare pro Connect locker 10 Sekunden Zeit, in denen Relais und ESP8266 sonst schon Strom ziehen würden.


Nachlese

Irgendwie haben kleine, eigentlich unkritische, Änderungen am Programm für den ATmega328 dazu geführt, dass die berechnete Anzahl von Tiefschlaf-Phasen nicht mehr stimmte. Laut Doku führt das Setzen der Register WDP3 und WDP0 dazu, dass 1.048.576 WDT Oscillator Cycles gewartet wird und dies bei VCC=5.0V einem Typical Time-out von 8.0 Sekunden entspricht. Die Batterie hatte zu diesem Zeitpunkt 5.07V (bzw. 5.03V mit einem anderen Multimeter).

Am ArduinoUNO (über USB versorgt) hat folgendes Progrämmchen
  char i;
  static unsigned long t1, t2;
  get_RTC();
  t1=RTC.time2000;
  for(i=0; i<100; i++)  {
    sleep_PWR_DOWN(); // ca. 8 Sekunden Tiefschlaf
  }
  get_RTC();
  t2=RTC.time2000;

  ESP8266_power_on_setup_and_wait_for_ip(10);
  ESP8266_connect_TCPIP(10);

  RTC.fillByTime2000(t1);
  snprintf(buf_g, 30, "INFO=%4d.%.2d.%.2d %.2d:%.2d:%.2d",
                RTC.year, RTC.month, RTC.day, RTC.hour, RTC.minute, RTC.second);
  TCPIP_send(buf_g);

  RTC.fillByTime2000(t2);
  snprintf(buf_g, 30, "INFO=%4d.%.2d.%.2d %.2d:%.2d:%.2d",
                RTC.year, RTC.month, RTC.day, RTC.hour, RTC.minute, RTC.second);
  TCPIP_send(buf_g);

  snprintf(buf_g, 30, "INFO=%4d", t2-t1);
  TCPIP_send(buf_g);

  ESP8266_disconnect_TCPIP();
  ESP8266_disable();

diese Ausgabe ergeben:
2015.11.14 10:41:35 Verbindung hergestellt ESP8266-2 (192.168.42.102)
2015.11.14 10:41:35 Info: 2015.11.14 10:27:53
2015.11.14 10:41:35 Info: 2015.11.14 10:41:31
2015.11.14 10:41:36 Info:  818
2015.11.14 10:41:36 Verbindung geschlossen ESP8266-2 (192.168.42.102)

Nach Einsetzen des ATmega328 in die Schaltung (mit obiger Spannung von 5.07V) kam dies:
2015.11.14 11:00:46 Verbindung hergestellt ESP8266-1 (192.168.42.101)
2015.11.14 11:00:46 Info: 2015.11.14 10:47:05
2015.11.14 11:00:46 Info: 2015.11.14 11:00:42
2015.11.14 11:00:46 Info:  817
2015.11.14 11:00:47 Verbindung geschlossen ESP8266-1 (192.168.42.101)

Also keineswegs 8 Sekunden - sondern vielmehr 8.18 bzw. 8.17 Sekunden.
Bei acht Stunden Wartezeit summiert sich so ein Fehler von 0.17 Sekunden auf
8[h]*60[m/h]*60[s/m]=28800[s]
28800[s]/8[s/z]=3600[z] Wartezyklen im Gegensatz zu
28800[s]/8.17[s/z]=3525.09[z] Wartezyklen.

3600[z]-3525[z]=75[z] Zyklen Unterschied
75[z]*8.17[s/z]=612.75[s] und somit
612.75[s]/60[s/m]=10.2[m] Minuten später als gewünscht.

Nicht ganz deuten kann ich, warum vorige Programm-Versionen am ArduinoUNO (mit seiner gleichbleibenden Spannung über USB) jemals pünktlich die Tiefschlaf-Phase verlassen haben....???  Wobei ich ihn da sicher nie acht Stunden habe warten lassen. Höchstens mal 30 Minuten. Aber auch dann wäre das eine Abweichung von immerhin 38 Sekunden. Hmmm...sehr sonderbar. Aber stimmt eigentlich, dass ich mich häufiger mal gewundert habe, dass er zu spät aufgewacht ist, obwohl ich die counter-Variable sogar mit 3 oder 4 initialisiert hatte.

Diese Erkenntnisse haben jedenfalls dazu geführt, dass das mittlerweile nicht mehr nötige Aufwachen um 01:00 Uhr jetzt dazu genutzt wird, die Dauer des sleep_PWR_DOWN() bei aktueller Batterie-Spannung auszumessen und diesen Messwert für die Berechnung der Anzahl der zu wartenden Tiefschlaf-Phasen zu nutzen. 100 Tiefschlaf-Phasen dauern 8.17*100/60=13.62 Minuten. Das passt also ganz locker in die Zeit zwischen 01:00 Uhr und 03:00 Uhr. Und erfreulicherweise kostet es keinerlei zusätzliche Energie aus dem Akku. Weil nach Restart nicht erstmal eine Viertelstunde nix passieren soll, wird in  setup() der Wert mit 8.3 bzw. 8300 initialisiert (kommt ja noch der Lebenszeichen-LED-delay hinzu) und dann erst in der folgenden Nacht korrigiert.

Zusätzlich wird jetzt am Ende der Routine zur Bestimmung der Dauer bis zur nächsten Aktion geprüft, ob die berechnete Dauer 12 Stunden übersteigt. In diesem Fall wird als Dauer 0 zurückgeliefert. Eine Dauer von fast 24 Stunden konnte vorkommen, wenn mehr als eine Minute nach der vorgesehenen Zeit nach der verbleibenden Zeit gefragt wurde. In diesem Fall hat die Routine nämlich bereits auf den Folge-Tag umgeschaltet.

Noch ist etwas Debug-Code im Programm für den ATmega328 aktiv.
Die Log-Einträge für einen TCPIP_sync_all() sehen derzeit so aus:
2015.11.19 03:00:03 Verbindung hergestellt ESP8266-1 (192.168.42.101)
2015.11.19 03:00:04 Autorisierung abgeschlossen
2015.11.19 03:00:04 Abweichung [sek]: 0
2015.11.19 03:00:05 Datum/Zeit übermittelt
2015.11.19 03:00:05 Zeiten-Tabelle ist aktuell
2015.11.19 03:00:05 Akku-Spannung: 5.25V
2015.11.19 03:00:05 Firmware-Version: 1.6.5
2015.11.19 03:00:05 Info: sleep_PWR_DOWN_duration_g=8350
2015.11.19 03:00:05 Info: sunw_g[0]=23
2015.11.19 03:00:05 Info: sunw_g[1]=14
2015.11.19 03:00:06 Info: sunw_g[2]=6
2015.11.19 03:00:06 Info: sunw_g[3]=52
2015.11.19 03:00:06 Info: sunw_g[4]=10
2015.11.19 03:00:06 Info: sunw_g[5]=2
2015.11.19 03:00:06 Info: sunw_g[6]=23
2015.11.19 03:00:06 Info: sunw_g[7]=15
2015.11.19 03:00:07 Info: sunw_g[8]=6
2015.11.19 03:00:07 Verbindung geschlossen ESP8266-1 (192.168.42.101)

Die erste Info-Meldung zeigt die zuletzt gemessene Dauer einer Tiefschlaf-Phase in Millisekunden.
Die folgenden Info-Meldungen zeigen die Sekunden bis zur jeweils nächsten Aktion im Moment des Aufwachens.
Der Log-Eintrag ist offensichtlich von der Sync-Aktion um 03:00 Uhr.
Somit sind die ersten drei Werte (23, 14, 6) also vor dem Runterfahren der Rollläden um 17:00 Uhr angefallen, die nächsten drei Werte (52, 10, 2) vor der Aktion um 01:00 Uhr und die letzten drei Werte (23, 15, 6) vor der Sync-Aktion, zu der die Log-Einträge gehören.
Die Aktions-Zeitpunkte sind:  15:00, 17:00, 01:00, 03:00
Die Zeit-Dauern dazwischen somit:  2h, 8h, 2h
Gut. Das passt ja verhältnismäßig zu: 23, 52, 23 ... auch wenn es nicht gerade sonderlich linear ist....
Aber ich könnte da schon noch etwas justieren, damit er vor jeder Aktion nur einmal zu früh aufwacht - also die Werte 14, 10 und 15 nicht mehr kämen.

Eigentlich sollte es reichen, die Variable counter mit 0 (statt 1) zu initialisieren. Aber andererseits will ich ja genau eine Tiefschlaf-Phase zu früh aufwachen.
Warum stimmt dieser blöde Wert nicht, wenn er doch nun schon täglich neu bestimmt wird und somit Änderungen der Batterie-Spannung kein Grund für Abweichungen mehr sein sollte...!?
In der Funktion zur Bestimmung der Dauer einer Tiefschlaf-Phase wird nach 100 Tiefschlaf-Phasen eine Sekunde addiert, um damit den Fall abzufangen, dass die Uhrzeit vor den 100 Tiefschlaf-Phasen ein paar Millisekunden vor Beginn der nächsten Sekunde abgefragt wurde. Wenn dann nach den 100 Tiefschlaf-Phasen wieder die Zeit abgefragt wird und dabei zufällig die Sekunde gerade erst ein paar Millisekunden alt wäre, ergäbe das einen Fehler für die Gesamtdauer von fast einer Sekunde.
Daher also plus eine Sekunde über alles. Für eine einzelne Tiefschlaf-Phase somit 1/100 Sekunde bzw. 10 Millisekunden.
Im Mittel bekomme ich dadurch einen um 5ms zu hohen Wert pro Tiefschlaf-Phase.
In acht Stunden summiert sich der Fehler auf zwei Sekunden, im Maximum (bei 10ms Fehler) auf vier Sekunden.

Mal überlegen, wie das bzgl. obiger Abweichungen hinkommen könnte ... hmmm ... irgendwie nicht ... dann müsste es linear ansteigen ... und das ist ja gerade nicht der Fall.
Bei höherer Anzahl an Tiefschlaf-Phasen wird der Fehler im Verhältnis kleiner.
Und dann gibts ja noch diese jeweils zweiten Werte. Nach zwei Stunden kommt er 23 Sekunden zu früh rein. 23/8,35=2,7545. Er könnte also noch zweimal schlafen, schläft aber nur einmal. Klar. Wegen der Initialisierung von counter mit 1 statt 0.
Nach acht Stunden kommt er 52 Sekunden zu früh rein. 52/8,35=6,2275. Von sechs mal schläft er offenbar nur fünf mal: (52-10)/8,35=5,03. Grund ist wieder die Initialisierung von counter mit 1.

Aber wieso will ich eigentlich, dass er eine Tiefschlaf-Phase zu früh aufwacht. Es langt ja, wenn er genau rechtzeitig aufwacht. Das "zu früh aufwachen" war seinerzeit schließlich nur zum Anglühen des ESP8266 gedacht und wäre damit jetzt überflüssig.
Die gemessene Tiefschlaf-Dauer wird nach oben angepasst und bei der Berechnung der Anzahl benötigter Tiefschlaf-Phasen werden Nachkommastellen rigoros abgeschnitten.
Damit sollte gewährleistet sein, dass er vielleicht zu früh, aber nie zu spät aufwacht.
Bei maximalem Fehler und maximalem Nachkommastellen-Verschnitt für acht Stunden Tiefschlaf also 4 Sekunden plus eine Tiefschlaf-Phase minus eine Millisekunde. Macht 12 bis 13 Sekunden, die er zu früh aufwachen könnte.
Dann kann ich mit gutem Gewissen die volle berechnete Anzahl von Tiefschlaf-Phasen warten bzw. counter mit 0 initialisieren.
Nun hatte ich ja aber den Eindruck, dass die RTC vorgeht.
Könnte das was an der Rechnung ändern oder verfälschen?
Der Zeitpunkt der Berechnung der Anzahl von Tiefschlaf-Phasen ist t0. Das ist ein Zeitpunkt und damit unbeeinflusst von einer vorgehenden Uhr.
Während der Tiefschlaf-Loop wird die Uhr nicht abgefragt. Erst beim Aufwachen. Da meldet die Uhr dann vielleicht eine Zeit, die nach der erwarteten (und tatsächlichen) Zeit liegt. Dadurch würde der Code-Block für die Aktions-Ausführung zu früh betreten werden. Das lässt sich aber ohnehin nicht ändern, weil die Schaltung ja nur die eine Uhr hat. Also egal ... und kein dramatisches Problem. Dann werden die Rollläden eben eine Sekunde zu früh angesteuert oder mit dem Python-Script gesprochen.

In setup() sollte ich sleep_PWR_DOWN_duration_g dann aber lieber mit 8400 initialisieren. Besser zu wenige Tiefschlaf-Phasen warten, als zu viele.


Ein vermeintlich neues Problem entstand, nachdem ich testhalber je einen debug_WLAN_send() direkt nach open_shutters() und close_shutters() eingebaut hatte. Dadurch hat sich der ATmega328 aufgehängt, keinen Reset gemacht und das Relais dauerhaft eingeschaltet gelassen.
Offenbar lag es daran, dass durch die Ansteuerung der IR-LEDs der 2200µF Kondensator komplett leergelutscht war.
Wurde dann bei leerem Kondensator das Relais und der 3.3V-Regler eingeschaltet, hats den ATmega328 ins Nirvana befördert. Kenne ich ja schon.
Nach Einfügen einer üppigen Kondensator-Auflade-Pause von einer Sekunde zwischen den Funktionsaufrufen war wieder alles gut.
Ich muss das mal im Auge behalten....vielleicht brauche ich doch noch einen Hardware-Watchdog-Timer (z.B. mit NE555), der nach spätestens 30 Sekunden ohne Retrigger (alias: Ansteuerung der Lebenszeichen-LED) einen Reset auf Pin-1 des ATmega328 schickt.

Das obige ESP8266-AnsteuerungV7.ino enthält bereits alle eben genannten Anpassungen.



Kaum schreibt man sowas, kommt beim nächsten Sync das hier:
2015.11.20 15:00:05 Info: sleep_PWR_DOWN_duration_g=8340
[....]
2015.11.21 03:00:03 Verbindung hergestellt ESP8266-1 (192.168.42.101)
2015.11.21 03:00:03 Autorisierung abgeschlossen
2015.11.21 03:00:04 Abweichung [sek]: 0
2015.11.21 03:00:05 Datum/Zeit übermittelt
2015.11.21 03:00:05 Zeiten-Tabelle ist aktuell
2015.11.21 03:00:05 Akku-Spannung: 5.23V
2015.11.21 03:00:05 Firmware-Version: 1.6.5
2015.11.21 03:00:05 Info: sleep_PWR_DOWN_duration_g=8380
2015.11.21 03:00:05 Info: sunw_g[0]=10
2015.11.21 03:00:06 Info: sunw_g[1]=2
2015.11.21 03:00:06 Info: sunw_g[2]=-41
2015.11.21 03:00:06 Info: sunw_g[3]=15
2015.11.21 03:00:06 Info: sunw_g[4]=6
2015.11.21 03:00:06 Verbindung geschlossen ESP8266-1 (192.168.42.101)

2015.11.21 07:00:05 Verbindung hergestellt ESP8266-1 (192.168.42.101)
2015.11.21 07:00:06 Info: open shutters
2015.11.21 07:00:06 Verbindung geschlossen ESP8266-1 (192.168.42.101)

2015.11.21 15:00:03 Verbindung hergestellt ESP8266-1 (192.168.42.101)
2015.11.21 15:00:04 Autorisierung abgeschlossen
2015.11.21 15:00:04 Abweichung [sek]: 0
2015.11.21 15:00:05 Datum/Zeit übermittelt
2015.11.21 15:00:05 Zeiten-Tabelle ist aktuell
2015.11.21 15:00:05 Akku-Spannung: 5.23V
2015.11.21 15:00:05 Firmware-Version: 1.6.5
2015.11.21 15:00:05 Info: sleep_PWR_DOWN_duration_g=8380
2015.11.21 15:00:05 Info: sunw_g[0]=54
2015.11.21 15:00:05 Info: sunw_g[1]=4
2015.11.21 15:00:06 Info: sunw_g[2]=110
2015.11.21 15:00:06 Info: sunw_g[3]=1
2015.11.21 15:00:06 Verbindung geschlossen ESP8266-1 (192.168.42.101)

Erste Auffälligkeit ist die Änderung der gemessenen Tiefschlaf-Dauer von 8340 auf 8380. In 24 Stunden um 40ms. Ganz schön happig.
Bei der Aktion um 17:00 Uhr ist er wie erwartet angekommen (10, 2). Da galt noch die Tiefschlaf-Dauer von 8340ms.
Auch bei der Aktion um 01:00 Uhr galt noch 8340ms. Da hat er aber um 41 Sekunden verpennt.
Danach wurde auf 8380ms umgeschaltet und daraufhin kam er um 03:00 Uhr 15 Sekunden zu früh aus dem Tiefschlaf.
Also zwei Sekunden über dem oben hergeleiteten Maximum.
Bei der Aktion um 07:00 Uhr ist er dann schon 54 Sekunden und um 15:00 Uhr sogar 110 Sekunden zu früh aufgewacht.
Das ist doch ein elender Mist.

Denkbar wäre, dass die Tiefschlaf-Dauer Temperatur-abhängig ist.
Ich habe gestern Abend nämlich den Ofen angemacht.
Die Schaltung steht schräg über dem Ofen, knapp einen Meter daneben und einen Meter höher als die Oberkante des Ofens.
Damit könnte das dort locker 10°C wärmer geworden sein - also so zwischen 18:00 - 01:00 Uhr.
Danach ist es langsam abgekühlt und die Tiefschlaf-Phasen wurden wieder kürzer.
Passt jedenfalls zu den Daten:
    01:00 - 03:00 (2h) -> 15 Sekunden zu früh
    03:00 - 07:00 (4h) -> 54 Sekunden zu früh
    07:00 - 15:00 (8h) -> 110 Sekunden zu früh
Tja, wenn das so ist, hilfts nix.
Ich werde der Schaltung deswegen jetzt sicher keinen LM35 zufügen und dann versuchen, aus der Temperatur einen Korrektur-Wert für die Tiefschlaf-Dauer abzuleiten. Dann lieber einfach 100ms auf die gemessene Tiefschlaf-Dauer aufschlagen und jeweils ein paar mal zu früh aufwachen.
Erstmal werde ich das jetzt mindestens eine Woche laufen lassen und danach anhand der gesammelten Logs entscheiden, ob ich doch noch was ändern muss.
Ein möglicher Ansatz wäre, die Tiefschlaf-Dauer bei jedem Aufwachen neu zu berechnen bzw. zu korrigieren.
Wobei das an diesem konkreten Verschlafen auch nix geändert hätte, da die Temperatur erst nach der vorigen Aktion (17:00 Uhr) angestiegen ist.


Ideen für eine mögliche nächste Version

Es sollte möglich sein, auf die RTC zu verzichten. Wenn ein- oder zweimal täglich das aktuelle Datum samt Uhrzeit per WLAN geholt wird, müssten sich ebenso gut die internen Zähler des ATmega328 (zur Zeitbestimmung über den Tag) verwenden lassen können.
Mit entsprechender Einarbeitung kann wahrscheinlich auch alles nur vom ESP8266 aus gesteuert werden. Ohne die RTC braucht es nur einen Ausgangs-Pin für die IR-LED. Zum ESP8266 käme also noch ein 3V-Akku, eine IR-LED, ein NPN-Transistor und zwei Widerstände hinzu. Theoretisch zumindest. Und dann könnte man das 5x bauen und jedem Schwenkwickler seine eigene Steuerung geben. Damit hätte man keine Kabel zu verlegen, immer ungehinderte IR-Kommunikation mit dem jeweiligen Schwenkwickler und lediglich gelegentliche Akku-Wechsel-Orgien.



Das Hintergrund-Bild dieser Seite ist übrigens nicht irgendwo geklaut, sondern vielmehr das (nur 7424 Byte große) Produkt aus einem Breadboard, einer Nikon D80 und GIMP 2.8.14.