Lichtwecker n+1, Arduino

Der chaotische Hauptfaden

Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese

Antworten
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Lichtwecker n+1, Arduino

Beitrag von Später Gast »

N'abend allerseits,

ich frickel grade an nem Lichtwecker und wollte euch am bisherigen Fortschritt teilhaben lassen, das Thema geistert hier ja auch immer mal wieder rum.

Hardwareseitig habe ich im Einsatz:

Arduino Nano v3(Klon)
DS3231
4Stelliges 7-Segment Display (weiß) an TM1637
DFplayer mini
20x4 LCD an I2C Expander (zu Diagnosezwecken, kommt wieder raus)
billiger Mosfet Treiberbaustein
2x 12v 10W Chinaled
Kühlkörper von Xana
12V 2,5A Steckernetzteil von Maxens

der Testaubau sieht so aus:
lichtwecker01.jpg
Der DFPlayer hat ne eigene Stromversorgung und versteckt sich hinter ner Menge Kabel

hier ist ne Skizze, wie das mal in etwa aussehen soll:
Der Druckknopf an der Front kommt noch nach rechts zum Encoder, ist besser weil man sonst bei der Bedienung die Hände wechseln muss oder mit der Hand übers Display fassen muss.
lichtwecker04.jpg
Am Gehäuse hab ich auch schon gewerkelt, ist aber noch nicht fertig.
Todo: Frontplatte, Ausfräsung für Plexiglasabdeckung, Holzplatte für den Reflektor einsetzen, schwarztransparent beizen, denke noch über Schellack nach, wahrscheinlich wirds aber klarlackiert. Das sieht auf dem Bild so aus, als wäre die vordere Kante senkrecht, sie ist aber etwa 30° nach hinten geneigt.
lichtwecker03.jpg
Der code ist Gegenstand aktueller Tests und müsste für Weckbetrieb angepasst werden, das fadet grad sehr schnell, ist aber zum Testen natürlich besser so. Hab versucht nen logarithmischen Fade hinzubekommen, ich bin auch der Meinung, das kommt so hin, nur ist am unteren Bereich des Fades der Unterschied so groß, dass man auch Wertsprünge von -+1 deutlich sieht.(dazu unten mehr)
Der Audioplayer startet im Moment direkt mit dem Fade, das werde ich noch anpassen, oder ich mach einfach 10 Minuten Stille an den Anfang der Sounds. Den Soundtrack hab ich von Freesounds.org. Geiler Scheiß, kann ich nur empfehlen sich das mal anzuhören, was es da so gibt. 8-) Grad jetzt, wo man nicht so viel rauskommt, mal angenehm, Natur hören zu können. :)
Was ich auch noch nicht programmiert habe ist, dass Sonnenaufgang 30Minuten vor der eingestellten Zeit beginnt.Kommt noch: ;)
03_Uhr_Alarm_nEnco - Kopie.zip
(12.96 KiB) 46-mal heruntergeladen
->umbenennen in *.ino

Dinge die noch nicht bei 100% sind:

4x7-Segment@TM1637 :evil: Sieht wirklich schön aus und ist sehr gut lesbar, aber ich habe noch keine Bib gefunden, die a) Meine Bedürfnisse erfüllt und b) tatsächlich das macht, was sie vorgibt. In Betrieb habe ich TM1637Display.h von hier. Ein Punkt der mich sehr geärgert hat, ist dass es so verkauft wird, als ob sowohl Dezimalpunkte, als auch der Doppelpunkt einzeln angesprochen werden können. Leider ist es so, dass nur entweder der Doppelpunkt, oder Die Dezimalpunkte funktionieren. Bei mir isses der Doppelpunkt. Das LED-Modul hat garnicht genügend Pins, damit man _alles_ ansprechen könnte, es sieht nur so aus als gäbe es Dezimalpunkte, wie doof ist denn das? Es gibt auf Ebay Displays mit nem anderen Controller, bei denen alles da ist, aber da steht nicht dran, welche Farbe die haben. :|
Ich wollte die Dezimalpunkte eigentlich zur Anzeige verschiedener Zustände nutzen, jetzt muss ich mir was andres überlegen. ich hätte die Front halt gerne so clean wies geht, extra LEDs verbauen ist doof. Fürs erste verwende ich den Doppelpunkt für Wecker an/aus, aber ich hätte den gerne dauerhaft an, sieht einfach besser aus bei ner Uhr.

Der andere Haken ist, dass ich es nicht schaffe, bestimmte Dinge anzuzeigen. Das Display soll zum Beispiel, wenn Button2 gehalten wird, die aktuelle Weckzeit anzeigen, und dabei entweder Minuten oder Stunden blinken lassen. (je nachdem, worauf der Encoder gerade eingestellt ist) Beim Blinken soll dann also gewechselt werden zwischen "12:34" und "12:_ _" Es gibt in der Library Beispiele, in denen das funktionieren sollte, aber die zeigen bei mir nicht das an, was sie sollen. Das zeigt dann "_ _:12" an, oder "_1:2_", oder "12:00". :(
Werd wohl nochmal ne andre Bibliothek ausprobieren müssen. Wahrscheinlich liegts auch eh nicht an der Bibliothek, sondern an meinem Display. :roll:

Zu dem stufigen Fade:
Mein Plan ist diesen kleinen Mister hier:
lichtwecker02.jpg
umzufrickeln, sodass die 2 Mosfetse nicht mehr parallel, sondern einzeln schaltbar sind. Dann kann ich über unterschiedliche Vorwiderstände die doppelte Auflösung rausholen und das sollte dann ja wohl reichen. Ich brauche ja nur im unteren Bereich mehr, ab spätestens 35 von 255 merkt man nix mehr. Ich könnte auch zwei von den Kraftzwergen nehmen, aber der eine langweilt sich ja jetzt schon bei den lumpigen 600 bzw 1200mA, die die LEDs bekommen. :|
Sollte ja gehen, oder hab ich da nen Denkfehler?


Das wars soweit von mir, wünsche noch einen schönen Sonntag Abend
Moritz
Anse
Beiträge: 2307
Registriert: Mo 12. Aug 2013, 21:30
Wohnort: Bühl (Baden)

Re: Lichtwecker n+1, Arduino

Beitrag von Anse »

Später Gast hat geschrieben: Mo 23. Mär 2020, 00:04 umzufrickeln, sodass die 2 Mosfetse nicht mehr parallel, sondern einzeln schaltbar sind. Dann kann ich über unterschiedliche Vorwiderstände die doppelte Auflösung rausholen und das sollte dann ja wohl reichen. Ich brauche ja nur im unteren Bereich mehr, ab spätestens 35 von 255 merkt man nix mehr. Ich könnte auch zwei von den Kraftzwergen nehmen, aber der eine langweilt sich ja jetzt schon bei den lumpigen 600 bzw 1200mA, die die LEDs bekommen.
Sollte ja gehen, oder hab ich da nen Denkfehler?
Wenn ich eins gelernt habe mit PWM und Licht, dann ist es eins: wenn möglich 16 Bit PWM verwenden. Dann kannst Du dir den doppel MOSFET sparen, weil Du dann 65536 Stufen statt 256 hast. So hab ich es bei meinem gemacht. Funktioniert super.

Ich halte 7-Segement Displays für einen Wecker für eher ungeeignet, da die Dimmung nur schwer möglich ist. Ich hab ein LCD mit integriertem Controller verwendet. Dann benötigt man nur noch einen PWM für die Hintergrundbeleuchtung. Beim Energieverbrauch sind LCDs auch deutlich besser. Zu dem gibt es welche, bei denen man eigene Zeichen kreieren kann.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Anse hat geschrieben: Mo 23. Mär 2020, 00:20 Wenn ich eins gelernt habe mit PWM und Licht, dann ist es eins: wenn möglich 16 Bit PWM verwenden. Dann kannst Du dir den doppel MOSFET sparen, weil Du dann 65536 Stufen statt 256 hast. So hab ich es bei meinem gemacht. Funktioniert super.
Stimmt, guter Einwand, würde ich sehr gerne machen, ist aber deutlich außerhalb meiner Programmierkentnisse. Ich lese OCR0 und OCRA, verstehe nur Bahnhof und hab Angst um mein heißgeliebtes millis(). :lol:
Hast du da nen Link wo ich das verstehe, oder womöglich was fertiges, was mir nicht den ganzen Sketch auf den Kopf stellt?
Ich halte 7-Segement Displays für einen Wecker für eher ungeeignet, da die Dimmung nur schwer möglich ist. Ich hab ein LCD mit integriertem Controller verwendet. Dann benötigt man nur noch einen PWM für die Hintergrundbeleuchtung. Beim Energieverbrauch sind LCDs auch deutlich besser. Zu dem gibt es welche, bei denen man eigene Zeichen kreieren kann.
Der Punkt Dimming ist nicht ganz von der Hand zu weisen, Stufe 1 von 7 ist im Dunkeln schon etwas zu hell, alles andere ist dann nur noch mehr zu heller. Ich schreib das auf die Liste für wenn das Problem aktueller wird. ;)
Stromverbrauch macht mir im Moment keine Sorgen, der ist auch mit dem 7Segment LED im Rahmen, außerdem werde ich den nicht aus dem Akku betreiben.
Nachdem Du jetzt den Punkt Dimming angesprochen hast, denke ich wieder über ne getönte Plexiglasscheibe vor dem Display nach. Da wollte ich mir was besorgen, um die bei Tageslicht leicht gelblich erscheinenden Segmente zu verstecken. Dann hab ich aber keine lokale Quelle gefunden und das wieder verworfen. Vielleicht einfach Tönungsfolie. irgendwas Dunkles halt. Das Ding stumpf mit 3,3V versorgen hilft schonmal ein bisschen. :twisted: Senkt auch den Stromverbrauch. :lol:

Die Lesbarkeit und der Kontrast ist bei 7 Segment mMn höher, es ist für mich aber auch ne ästhetische Frage, ich möchte gerne große, leuchtende Lettern mit hohem Kontrast haben. Ich möchte nur Zahlen darstellen. Wenn ich nicht dieses 7Segment Display nehme, dann nehme ich ein größeres für noch bessere Lesbarkeit bei nächtlich verklebten Schmieraugen. K.I.S.S. :P
Der soll nur 2 Sachen können, aber die dafür gut. :)
ch_ris
Beiträge: 3042
Registriert: Mo 30. Nov 2015, 10:08

Re: Lichtwecker n+1, Arduino

Beitrag von ch_ris »

vielleicht mach ich sowas auch mal.
dann macht ich für die uhrzeit ein schiebepoti dran.
wenns genau sein muss bzw. zur kontrolle, serielle Konsole über handy.
Benutzeravatar
Fritzler
Beiträge: 12602
Registriert: So 11. Aug 2013, 19:42
Wohnort: D:/Berlin/Adlershof/Technologiepark
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Fritzler »

Anse hat geschrieben: Mo 23. Mär 2020, 00:20 Ich halte 7-Segement Displays für einen Wecker für eher ungeeignet, da die Dimmung nur schwer möglich ist.
Halt ich fürn Gerücht =P
-> http://fritzler-avr.de/HP/tipps/multi.php
Benutzeravatar
sukram
Beiträge: 3116
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Lichtwecker n+1, Arduino

Beitrag von sukram »

Fritzler, wenn du das Latch und den BCD Decoder durch HC595 ersetzt, kannst du mit nur 4 Pins die Segmente beglücken. Musst aber dann die Daten etwas anders aufbereiten und als 16Bit Wort über SPI prügeln.
Benutzeravatar
Fritzler
Beiträge: 12602
Registriert: So 11. Aug 2013, 19:42
Wohnort: D:/Berlin/Adlershof/Technologiepark
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Fritzler »

Ja, aber son uralt AVR hat meist nur 1 SPI und den muss man damit jetzt nicht vollballern.
Mehrere SW Teile auf 1 SPI loslassen geht zwar, aber dann kannste dir schön Semaphoren bauen und der Multiplexer muss immer vorrang haben.

Seidenn es ist die einzige SPI Ausgabe und das Sreg ist sehr groß :twisted:
http://fritzler-avr.de/HP/ledscreen.php
Anse
Beiträge: 2307
Registriert: Mo 12. Aug 2013, 21:30
Wohnort: Bühl (Baden)

Re: Lichtwecker n+1, Arduino

Beitrag von Anse »

Später Gast hat geschrieben: Mo 23. Mär 2020, 01:29 Stimmt, guter Einwand, würde ich sehr gerne machen, ist aber deutlich außerhalb meiner Programmierkentnisse. Ich lese OCR0 und OCRA, verstehe nur Bahnhof und hab Angst um mein heißgeliebtes millis().
Hast du da nen Link wo ich das verstehe, oder womöglich was fertiges, was mir nicht den ganzen Sketch auf den Kopf stellt?
Achtung, graue Theorie: PWM wird über Timer gemacht. Davon hat der Atmega328, welcher auf dem Bord sitzt, drei Stück. Allerdings kann das Ausgangssignal nur an jeweils zwei festen Pins ausgegeben werden. In Deinem Fall D9 oder D10 (OC1A und OC1B).
Die Konfiguration des Timers ist relativ einfach:

Code: Alles auswählen

DDRB|=(1<<1);//Direction von D9 auf Output
TCCR1A=(1<<COM1A1)|(1<<WGM10)|(1<<WGM11);//Fast PWM 
TCCR1B=(1<<WGM12)|(1<<WGM13)|(1<<CS12); //3900Hz (16e6/2^16/256)
OCR1A=3000;//Der aktuelle PWM Wert (0-65535)

Der Code muss irgend wo ins Setup. Hoffe mal, im Arduino Header sind die Register definiert. Wenn ich jetzt nichts gravierendes übersehen habe, sollte an D9 ein PWM Signal erscheinen.
Das feine daran neben der deutlich besseren Auflösung ist, dass die PWM Erzeugung vollständig im Hintergrund geschieht und keinerlei CPU Takte klaut. Lediglich der Wert von OCR1A muss geändert werden, um die Pulsweite zu verändern.

Achtung, noch was: Das Arduino Framework nutzt wohl eine Timer als Systemzeitreferenz. Es kann sein, das Funktionen, welche drauf angewiesen sind jetzt nicht mehr funktionieren.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Anse hat geschrieben: Mo 23. Mär 2020, 23:12

Code: Alles auswählen

DDRB|=(1<<1);//Direction von D9 auf Output
TCCR1A=(1<<COM1A1)|(1<<WGM10)|(1<<WGM11);//Fast PWM 
TCCR1B=(1<<WGM12)|(1<<WGM13)|(1<<CS12); //3900Hz (16e6/2^16/256)
OCR1A=3000;//Der aktuelle PWM Wert (0-65535)

Der Code muss irgend wo ins Setup. Hoffe mal, im Arduino Header sind die Register definiert. Wenn ich jetzt nichts gravierendes übersehen habe, sollte an D9 ein PWM Signal erscheinen.
Das feine daran neben der deutlich besseren Auflösung ist, dass die PWM Erzeugung vollständig im Hintergrund geschieht und keinerlei CPU Takte klaut. Lediglich der Wert von OCR1A muss geändert werden, um die Pulsweite zu verändern.

Achtung, noch was: Das Arduino Framework nutzt wohl eine Timer als Systemzeitreferenz. Es kann sein, das Funktionen, welche drauf angewiesen sind jetzt nicht mehr funktionieren.
Danke! Das mit dem keine CPU-Takte klauen ist auch gut, man sieht die Stufen nämlich auch deswegen gut, weil jedes Neusetzen der PWM-Frequenz n Fitzel Zeit braucht und man die Aussetzer bei wenig Licht sehen kann.

Wenn ich das richtig gegoogelt hab, benutzt das Timer1, der ist für PWM und tone() zuständig, letzteres benutz ich nicht und PWM will ich ja ersetzen. Probleme gäbe es, wenn ich Timer0 verändern würde, der macht millis() und delay() und insbesondere millis() würde mir große Probleme bereiten. Ich probier das mal aus und berichte! :)
Anse
Beiträge: 2307
Registriert: Mo 12. Aug 2013, 21:30
Wohnort: Bühl (Baden)

Re: Lichtwecker n+1, Arduino

Beitrag von Anse »

Bitte, bin mal gespannt, ob es so klappt.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Okay, ich habs jetzt mit diesem Code hier ans Leufen gebracht:

Code: Alles auswählen

void setup() {
  Serial.begin(9600);
  setupPWM16();
}

uint16_t icr = 0xffff;

void loop() {
  Serial.println("*");
  for (uint16_t i = 0; i < 2000; i++)
  {
    analogWrite16(9, i);
    delay(2);
  }
}

void setupPWM16() {
  DDRB  |= _BV(PB1) | _BV(PB2);       /* set pins as outputs */
  TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                 /* mode 14: fast PWM, TOP=ICR1 */
  TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                  /* prescaler 1 */
  ICR1 = 65535;                         /* TOP counter value (freeing OCR1A*/
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */

void analogWrite16(uint8_t pin, uint16_t val)
{
  switch (pin) {
    case  9: OCR1A = val; break;
    case 10: OCR1B = val; break;
  }
}
Den hab ich von hier. Das Problem war erst, dass

Code: Alles auswählen

#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
SoftwareSerial mySerial(7, 8); // RX, TX
DFPlayerMini_Fast myMP3;
irgendwas mit Pin9 veranstaltet. In deinem Code war ich mir nicht sicher, wie man den Pin ändert. (Womöglich mit "DDRB|=(1<<2);"?) Auf Pin 10 läufts. Jetzt muss ich nurnoch den Code für die neuen Zahlen fit machen. 8-)
Anse
Beiträge: 2307
Registriert: Mo 12. Aug 2013, 21:30
Wohnort: Bühl (Baden)

Re: Lichtwecker n+1, Arduino

Beitrag von Anse »

So müsste es für Pin 10 aussehen. Allerdings, wenn Du eine funktionierende Lösung hast ist das noch besser ;)

Code: Alles auswählen

DDRB|=(1<<2);//Direction von D10 auf Output
TCCR1A=(1<<COM1B1)|(1<<WGM10)|(1<<WGM11);//Fast PWM 
TCCR1B=(1<<WGM12)|(1<<WGM13)|(1<<CS12); //3900Hz (16e6/2^16/256)
OCR1B=3000;//Der aktuelle PWM Wert (0-65535)
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

So, bin hier mittlerweile so einiges weitergekommen.

-Auch mit 16Bit PWM kann ich beim Dimming am Anfang noch sehr gut Stufen erkennen. Stört mich aber nicht. Ich habe außerdem das Gefühl, dass das Setzen der PWM Zeit benötigt und in dieser Zeit ist der Ausgang 0. Ich meine jedenfalls kleine Wackler zwischen den Steps zu sehen.

-Dass die serielle Kommunikation mit dem dfPlayer Probleme an Pin 9 verursacht hat, lag natürlich an einem Lötfehler.

Hab von PullDown auf die interne PullUp Funktion umgestellt. Im Moment sind noch keine Serienwiderstände an den Schaltern, das hab ich aber vor. Wie sollte ich die bemessen?

-Das Display lässt sich sehr gut an 3,3V betreiben, die sind aber immernoch zu hell. 2,8 wären bei dunkler Raumumgebung optimal. Beim Experimentieren mit einer Diode in Serie hab ich rausgefunden, dass die 3,3V vom Nanoklon in wirklichkeit 3,8 sind. Ein anderer Nano den ich hier rumliegen hab macht 3,7V, hätte also nichts verbessert. mit 2 Dioden war die Spannung zu klein, mit einer zu groß. Nach etwas fruchtlosem rumhirnen und Käseschaltungen mit Dioden und Widerständen hatte ich die glorreiche Idee, die 16Bit PWM vom zweiten Pin zu nehmen, gleichzufiltern und als Versorgung zu verwenden. Der Nano kann pro Pin maximal 40mA, hab dann für das RC-Glied 100 Ohm genommen, dann fließen maximal 25mA, 1000µF als C und zack feddich ist die Dimmfunktion. Das kann jetzt wirklich große Helligkeitsunterschiede abdecken. Die Interne Dimmingfunktion verwende ich aber weiter, weil das Display bei knapper Spannungsversorgung sonst zu Flimmern beginnt. Das Display ist übrigens überhaupt kein Stromfresser. Auf volle Lotte nimmt sich das ~17mA, aber bei normalen Leuchtstärken reichen 3-10.

Damit ich von der Dimmfunktion auch was hab, hab ich nen Fotowiderstand mit nem 5k Widerling zu nem Spannungsteiler verheiratet und kann jetzt die Helligkeit der Umgebung automatisch anpassen.

Am Gehäuse bin ich auch soweit fertig. Leider bin ich im Planen nicht so strukturiert, und habe vorher nicht überlegt, wie ich die Lautsprecher im Gehäuse festschrauben will, wenn das Gehäuse mal zusammengebaut ist. Aber wer kennt nicht die alte Bastlerweisheit, "wer es nicht im Kopf hat, nimmt Heißkleber" :mrgreen:
2lichtwecker02.jpg
Passiert sowas eigentlich nur mir?
2lichtwecker04.jpg
Geht ganzschön eng zu dadrinnen. Ich verschätz mich immer beim Platzbedarf der Elektronik. Hier ist es nochmal gutgegangen. Ohne Heißkleb wär ich aufgeschmissen. ;)
2lichtwecker05.jpg
Die LED sind mit Endsieg befestigt. Hatte davor den Plan, das zu schrauben, aber der ist am nicht vorhandenen m2 Gewindeschneider gescheitert. Für Federklemmen ist da kein Platz.
Außerdem zu sehen der Fotoresistor. Hatte keine Lust, ein extra Loch in die Front zu bohren. Mach ich die Lichtmessung während des Weckvorgangs halt aus. Der Mosfet und die Vorwiderstände sowie eine 2A Sicherung sind unterm Reflektor. Da geh ich nochmal ran, wollte noch ne kleine Patine für die Widerlinge spendieren (ist noch Freiluftverdrahtung) und die Werte etwas nach oben anpassen. die haben jetzt 0,9 Ohm, da fließt bei 12,2V Versorgung, die aus dem Netzteil kommen 600mA und die steigen bei längerer Vollast nochmal deutlich an. Bei 40°C am Aluträger warens schon 2x700mA und hier wirds gut warm im Sommer, da können das sicher auch mal 60°C werden. Können sollten sie 1A pro Stück, da käme dann die Sicherung, aber da muss ichs ja nicht drauf ankommen lassen und das macht WIRKLICH gut Hell. *blinzel* :twisted: 8-) Ich kann also etwas Reserve verschmerzen.
2lichtwecker06.jpg
Hirnlose Käseschaltung. :roll:
2lichtwecker07.jpg
Da hab ich das Gehäuse zum ersten Mal fertig aufgebaut, es fehlt aber noch die Mattierungsfolie für die Plexiglasscheibe.
2lichtwecker01.jpg
Mehr gibts später, hier gibts jetzt Futter.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Okay, ich hab etwas mit dem armeleuteDAC rumgespielt und finds echt super. Das macht jetzt das Display so dunkel, dass es im gedimmten Zustand bei heller Umgebung aussieht, als sei es aus. Das lässt sich merkwürdiger Weise nicht wirklich fotografieren, das sieht immer heller aus, als es ist. Jedenfalls ist der Dimmbereich jetzt groß genug und fein aufgelöst, sodass es bei Nacht nicht unangenehm blendet. Die ungünstige Sensorposition merkt man schon etwas, aber dafür hab ich eine Kompensationsvariable eingebaut, mit der man es auf den Standort anpassen kann. So'n Lichtwecker wird ja nicht die ganze Zeit rumgetragen.

Der dfPlayer rauscht etwas, obwohl er eigentlich im Sleepmodus sein müsste, das sah mir schon beim Stromverbrauch merkwürdig aus. das muss ich mir nochmal genauer anschauen. Klanglich ist das aber echt in Ordnung. wer mit mono auskommt ist damit echt gut bedient.Ich hab 2 3W Speaker in Reihe dran und bei basslastiger Musik kriegen die hörbar zuviel auf Vol=30. Den Fade-in vom Sound mach ich in der MP3, da hab ich mehr Kontrolle und bessere Möglichkeiten. Geplant sind noch mehr Auswahl an verschiedene Aufwachgeräuschen wie anderes Vogelgezwitscher, oder Meeresrauschen, aber wirklich spannend fänd ich randomisiert mal mit Gewitter geweckt zu werden, oder mit nem Aufwachenden Festival. HEEELGA! :mrgreen:
Noch besser wenn er sich online das Wetter holt und den passenden Sound abspielt. Aber da bräuchts dann wohl nodeMCU, soweit bin ich noch nicht.

Bin grad noch an den Zeitlichen Abläufen verhakt, ich hab wegen meiner Matheminderbemittelung ;-P den Logarithmus etwas blöd implementiert und kann daher nicht ausrechnen, wie oft die Routine aufgerufen werden muss, bis sie bei #Endwert ankommt. Hab das daher experimentell ermittelt und dann passend hinmultipliziert. Ich steig grade selber nicht mehr durch. das könnte man bestimmt eleganter lösen und ich werd das auch nochmal durchbürsten, aber fürs erste tuts. :roll:
So ganz knickfrei kommt mir das trotzdem nicht vor. Das ist auch garnicht so leicht zu 'sehen' son Helligkeitsverlauf mit so großen Intensitätsunterschieden.

Code: Alles auswählen

void alarm(){
  //Sonnenaufgang: (neuer Fade:)  nach #Zeitintervall wird der bisherige Helligkeitswert mit 1,01 multipliziert. 
  //20Minuten Fade
  //MP3Start nach 10Minuten
  //Anpassung auf 16Bit PWM
  
  
const int upSpeed=944;    //(944neu=>20 Minuten, 100=>127sek.) sets Interval for multiplication of fadeVal(fade in)
                          //kleinere Werte=schnellerer Fade 
const int dnSpeed=6;      //(6) sets Interval for division of fadeVal (fade out)
const byte lowMinim=15;   // Offset für Einstellung der niedrigsten Helligkeitsstufe, damit der langsame Teil des Fades auch im nützlichen Helligkeitsbereich ist
unsigned long fadeTime=(millis()-fadePointer);
// nicht mehr benötigt: fadeTime=constrain(fadeTime, 0, 1800000); 

//Alarm Stufe 1: Sonnenaufgang, Gezwitscher
  if (AlarmState==1 && fadeTime < 1200000){
    //Start Audio
    if(fadeTime>600000 && playing==0){                      //10min nach Beginn(900K Millisekunden) 
       myMP3.wakeUp();                                      //soll nur einmal ausgeführt werden
       delay(10);
       myMP3.play(1);                                       //spielt Vogelgezwitscher,  
       playing=1;                                           //
        }
    if ((fadeZaehl+upSpeed)<fadeTime && fadeULong<4901480){   //Funktion wird ausgeführt, solange keine 5M erreicht werden können
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong*1.01);                               //
      fadeVal=fadeULong/76.3+lowMinim;                                 //5000000/76,3= 65535, der 100%wert für PWM, map() macht mist    
      fadeVal=constrain(fadeVal, 0, 65535);
        }
   analogWrite16(ledPin, fadeVal);     
  }
  //Immernoch Stufe 1 aber Vollgas Licht jetzt, Exit to 2
  if (AlarmState==1 && fadeTime > 1199999){    
   if (fadeTime > 1199999){                                   //wenn nah genug an 5M, weiter bei AlarmState2, =5M
      fadeULong=5000000;                                        //maximum Warp
      fadeVal=65535;
    }
  // Maximum, ist erreicht(1800000ms/30 Minuten preset)
    if (fadeTime>1799999){
        AlarmState=2;
      }
   analogWrite16(ledPin, fadeVal);
  }


//Alarm Stufe 2: Vollgas Licht, laute Weck-Musik 
  if(AlarmState==2){                                    //schläft noch&& bisherige Maßnahmen zeigten keine Wirkung
     fadeULong=5000000;                                      //maximum Warp
      fadeVal=65535;
     if(playing!=2){
       myMP3.play(2);                                   //spielt laute Weckroutine  
       playing=2;
       }
      if (fadeTime>2400000){
        myMP3.play(2);     //<-Track erstellen und reinkopieren, nummer hierhin                  //35minuten Neuer, aggressiver Wecktrack, volle Lautstärkespielt laute Weckroutine  
        playing=2; 
      }
      if (fadeTime>2100000){                            //40 Minuten. genug geweckt, ist keiner Da-> ausschalten!             
      fadeULong=100;
      fadeVal=0;
      analogWrite16(ledPin, 0);
      fadeZaehl=0;
      AlarmState=0;
      playing=0;
      myMP3.sleep();
      AlarmV=false;
      }
    analogWrite16(ledPin, fadeVal);
  }

 //Alarm Stufe3:  ist wach, braucht aber noch Licht, Fadeval bleibt auf bisherigem Wert (||fester, angenehmer Wert?)
  if(AlarmState==3){                                    
    if(playing!=3||playing==1){
       myMP3.play(3);                                   //angenehme leise Musik  
       playing=3;
       }
  analogWrite16(ledPin, fadeVal);
  }

 //Alarm Stufe 4: Es wird aufgestanden, Licht kann ausfaden, kleiner Soundschnipsel als Bestätigung 
  if(AlarmState==4&&fadeULong>100){                         //steht jetzt auf Licht soll ausfaden
    if(playing!=4){
       myMP3.play(4);                                     //sound +1Up Mario
       playing=4;
       }
    if ((fadeZaehl+dnSpeed)<fadeTime){
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong/1.01);  
      fadeVal=(fadeULong/76.3);
      fadeVal=constrain(fadeVal, 0, 65535);
      analogWrite16(ledPin, fadeVal);
      }
    
  }
  if(AlarmState==4&&fadeULong<101){                          //Fade ist durch, alles zurück auf Ausgangswerte/abschalten
   fadeULong=100;
   fadeVal=1;
   analogWrite16(ledPin, 0);
   fadeZaehl=0;
   AlarmState=0;
   playing=0;
   myMP3.sleep();
   AlarmV=false;
      }
  
  
  }
und im loop steht noch

Code: Alles auswählen

if(AlarmV==true &&AlarmState==0 && hours==rAhours && mins==rAmins){      //30min früher als Amins/Ahours wg Dauer d Sonnenaufgang 
        fadePointer=millis();
        
        und
        if(AlarmState>0){
    alarm();
    }
        
Ich mach morgen wenn die Sonne wieder da ist nochmal Fotos, versprochen :)
Anse
Beiträge: 2307
Registriert: Mo 12. Aug 2013, 21:30
Wohnort: Bühl (Baden)

Re: Lichtwecker n+1, Arduino

Beitrag von Anse »

Sieht gut aus :D

Noch ein Wort zum Code. Hab nur schnell drüber geschaut. Wieso das PWM so spring kann ich Dir nicht genau sagen. Kann auch an der menschlichen Wahrnehmung liegen. Bei geringeren Helligkeit werden auch kleine Änderungen stark wahrgenommen.

Die Berechnung der Werte zur Laufzeit ist eigentlich unüblich. Hier ist es aber wahrscheinlich egal, da der µC sich eh langweilt und eigentlich schon den nächsten Schritt vorbereiten könnte, während er wartet.
Ich hab das damals mit einer Look Up Table gelöst. So konnte ich jeden beliebigen Helligkeitsverlauf einstellen. Man könnte sogar den örtlichen Sonnenaufgang mit loggen und verwenden ;)

Nächster Schritt, Schlafphasen Wecker, oder?
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Ah stimmt, ein Array zu nehmen hatte ich zu Beginn überlegt, aber dann bin ich in Calc daran gescheitert, mir den Verlauf logarithmisch zu erechnen. Von Hand war mir natürlich zu blöd das auszurechnen, außerdem war das doof mit nur 255 Werten. große Sprünge am Ende einer grob aufgelösten Skala. :|
Ich dachte dann auch, ich müsste ja eigentlich nur die Funktion umkehren, also wieviel y für x, damit ich wenigstens immer nur Sprünge von +-1 hätte, aber in unterschiedlich viel Zeit und das war dann völlig außerhalb meiner Fähigkeiten. :?
Also hab ich angefangen runzusuchen und bin auf diese Idee gestoßen.¯\_(ツ)_/¯
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Anse hat geschrieben: Mo 30. Mär 2020, 23:35 Ich hab das damals mit einer Look Up Table gelöst. So konnte ich jeden beliebigen Helligkeitsverlauf einstellen. Man könnte sogar den örtlichen Sonnenaufgang mit loggen und verwenden ;)
Okay, mir ist was eingefallen, wie ich mir den Array erstelle, ich lass mir das über die serielle Schnittstelle ausgeben. Da kann ich dann auch gleich die passende Formatierung mit ausgeben lassen.
Das Gewackle bei den Stufen ist wahrscheinlich wirklich ein Wahrnehmungsproblem. So wie Nachbilder, wenn man zu lange in was Helles blickt.
Nächster Schritt, Schlafphasen Wecker, oder?
hmm. Geraffel kommt mir beim Schlafen keins an den Körper und Sensorik am Bett ist mir glaub etwas zu umständlich. Der Lichtwecker macht ja im Grunde das gleiche. Es wird eine unterschwellige Störung erzeugt, die bei geringerer Schlaftiefe/Wachphase bemerkt wird. Der schlafphasen Wecker machts andersrum. Wachphase detektieren und dann gezielt stören. Ich glaube ich mag den Lichtwecker lieber, weil er eben unterschwellig arbeitet und nicht hart eingreift. Man hat die Wahl sich nochmal umzudrehen, ohne sich in die Snooze-Hölle zu begeben. ;)

Außerdem hab ich da noch genug dran zu optimieren. Der dfPlayer schläft nicht und das Display macht auch nicht was es soll. (blinkende Digits, Dezimalpunkte) Ich hab eh den Plan, nen anderen DCDC Wandler einzusetzen, allerdings ziept der Player ein bißchen beim Powerup, sprich einfach nur den Wandler per Enable zu schalten ist blöd, weil das Lärm macht.

Die Reallife-Tests bisher standen unter keinem guten Stern. Ein mal dem Wecken unfassbar schlecht geschlafen und als er dann klingelte erstmal ausgeschaltet und 1,5h weitergepennt. :lol: Heute dann 3 Stunden vor dem Wecker wachgewesen. So wird das nix. :|

Bin auch noch nicht zum Bildermachen gekommen :oops:
lüsterklemme2000
Beiträge: 259
Registriert: So 28. Aug 2016, 20:31
Wohnort: Südliches Niedersachsen

Re: Lichtwecker n+1, Arduino

Beitrag von lüsterklemme2000 »

Schlafphasen nachrüsten ist kein großer Aufwand, da reichen zwei Dehnungsmessstreifen aus einer Personenwaage unter den Füßen vom Bett. Das habe ich vor ein paar Jahren mal gemacht. Die Software überwacht die Schwankungen der Gewichtsverteilung und weckt ab einem bestimmten Grenzwert. Ich kann wenn gewünscht die Tage mal die Software dazu suchen.
Benutzeravatar
Bastelbruder
Beiträge: 11559
Registriert: Mi 14. Aug 2013, 18:28

Re: Lichtwecker n+1, Arduino

Beitrag von Bastelbruder »

Zwei Dehnungsmeßstreifen, was für ein Aufwand!
Da war mal eine Geschichte mit Milchschüssel und Kochlöffel an den Matratzenfedern. :lol:

Ein Pendel mit einem kleinen Magnet, und drunter liegt eine Spule. Damit hab ich vor gut 35 Jahren eine Autoalarmanlage mit Funkalarmierung gebaut. Die Idee war aus Elektor.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Naja, die Sensorik am Bett ist eine Sache, wobei mein Bett in KA keine Füße hat,(und keinen Bodenkontakt, an der Wand montiertes Hochbett) aber das ließe sich sicher am Lattenrost umgehen. Man muss die Sensoren aber auch irgendwie an den Wecker weiterleiten und ich möchte abgesehen vom Netzkabel da nichts reinführen, das hieße dann separate Stromversorgung und Funkmodule.

@Lüsterklemme: Danke für das Angebot, aber im Moment möchte ich das Dingens erstmal konsolidieren. Da ist wirklich noch genug dran, was noch nicht da ist, wo ich es haben will, und jetzt möchte ich erstmal rausfinden, wie ich das finde, vom Lichtwecker geweckt zu werden, und wie das für mich zu sein hat. Wenn das dann fertig ist, bleibt immernoch Platz für "feature creep". ;)

Habe nur grade das "Problem" keinen Wecker zu brauchen, wenn die Sonne schon oben ist, merkt man von den 14W LED-Power gar nicht mal so viel. Die müssten dann schon direkt von oben kommen, die Jalousien lassen zu viel Sonnenlicht durch. Das langsam lauter werdende Gezwitscher ist aber wirklich nice, langsames Aufwachen ohne Weckschock. 8-)
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Ich hab mal in meinem Kaninchenbau weitergebuddelt. :roll:

Ich habe jetzt endlich auch mal welche von den guten DCDC-Wandlern erhalten und eingebaut. ursprünglich war der Plan, dass der Arduino den Wandler bei Bedarf (Audio/Musik soll abgespielt werden -> dfPlayer braucht saft) über Enable anmacht. Dafür muss man dann aber auch am dfPlayer den Enable vom Verstärker betätigen, weil sonst bei jedem mal an/aus Gepupse aus den Lautsprechern kommt. Der dfPlayer macht mit dem Pin nichts (Der hängt dauerhaft mit 0 Ohm auf Masse=An), der 0Ohm Widerling muss also weg und da kommt dann der Enable Ausgang vom Arduino hin. Ist etwas hakelig, weil son SMD-Lötpäd nicht viel aushält und die Leitung ja irgendwie zum Arduino geführt werden muss. Ich hab das mit ner Zugentlastung aus Heißkleb gelöst. schöner wäre, einen der Pins vom dfPLayer umzuwidmen, da gibts ja genug, die hier nicht benutzt werden, war mir aber zu umständlich.

Da hab ich dann verschiedene Möglichkeiten durchprobiert, die Variante mit dem geringsten Stromverbrauch war, den Arduino am Wandler zu haben, der ist dann natürlich immer an, der dfPlayer wird über Diode +Elko versorgt (dfPlayer möchte am liebsten 4,2V und nicht 5), der Amp wird nur bei Bedarf aktiviert über den herausgeführten Enable.
Damit hab ich jetzt nur nen leisen Knackser, wenn Audioplayback aktiviert wird. Das passiert, wenn ich noch schlafe, stört also nicht. Langfristig kommt da noch ein Stereo-Amp dran, (PAM-irgendwas) dann werd ich hoffentlich auch den Knackser los.

Ich hab nochmal n anderes Display ausprobiert, mit ht16k33, wegen der indicator-Dots. Die Library von Adafruit ist riesig und umständlich, mit ner anderen gehts ganz gut, Aaaaber: Der gehackte Displayhelligkeits-DAC bringt da nichts, weil aus den Datenpins Strom in die Versorgung leakt und die damit immer bei 100% ist, selbst wenn aus dem PWM-Pin garnichts kommt. Widerstände in der Datenleitung sind entweder zu klein oder verhindern die Kommunikation. Das Display hat zwar 16 Helligkeitsstufen, aber die dunkelste ist wieder mal ziemlich hell, macht also keinen Spaß. Außerdem ist das Rot, das gefällt mir nicht. Muss wohl doch noch ne Led in die Front rein, am besten auch mit Dimming. -> toDo

Am Code hab ich auch nochmal einiges getan, kleine Bugs ausgemerzt und ne Lampenfunktion mittels Doppelklick implementiert. Die Helligkeit wird dann mit dem Encoder eingestellt, man kann ihn auch als Nachtlicht verwenden.
Das mit der Lookup table/Array hab ich gelassen, weil kein Platz! :shock:
Mein Sketch hat jetzt 93% ( Nano v3 mit ATmega168), Serielle Kommunikation zum PC musste ich schon rauskanten. Das Meiste werden wohl die Libs sein, aber ohne die bekomm ichs nicht hin.
Bei der aktuellen Auflösung (1/sekunde, 30minuten, 16Bit) bekomm ich das auf keinen Fall unter.

Ich hätte gerne dass der Encoder bei schnellem Drehen beschleunigt, dann müsste man nicht mehr zwischen Stunden und Minuten umschalten, da mach ich vllt noch was dran.

Es gibt es 4 Weckphasen,mit jeweils eigenen Soundtypen, aufgeteilt in eigene Verzeichnisse:
1. Ambient Sounds zum Aufwachen. Der Fade wird in die MP3Datei geschrieben, damit hab ich dann 16bit Auflösung ohne weitere Verrenkungen. Sounds haben 20 Minuten, der Fade geht über die komplette Länge. Der Alarm dauert 30 Minuten, nach 12 Minuten startet Audioplayback
2. aggressives Weckaudio immernoch zum Auffwachen, falls der Schläfer nach Ablauf von 1. noch hochohmig ist
3. angenehme Weckmusik. Ausgewählte Musik, die ich gerne zum Aufwachen hören möchte, Licht wird auf 30% gedimmt mit zwischenFade
4. kurzer Bestätigungssound, dass Alarm abgeschlossen ist (zb Super Mario +1Up), Licht->Fadeout

In jeden Ordner können max 255 Dateien rein, die werden dann zufällig ausgewählt, dann hat man nicht immer den gleichen Gockel der so nervig kräht, sondern etwas mehr Vielfalt. Die Anzahl der Dateien werden in Setup abgefragt, man kann also einfach beliebige Dateien dazupacken und sie werden automatisch berücksichtigt. Weil mir random() die Tracks immer in der gleichen Reihenfolge präsentiert hat, hab ich randomSeed im Setup, Quelle ist millis()+Usereingabe, nach Powerup zeigt das Display erstmal nur millis() an, bis man auf den großen Knopf drückt. Den muss man auch halten wenn man die Weckzeit einstellen will.
Während der Alarmphasen 1 und 3 wird abwechselnd verbliebene Weckzeit bis t=0 und aktuelle Uhrzeit angezeigt. Bei Phase 1 sind das 30 Minuten, bei Phase 3 ist der Proband schon wach und hat 10 Minuten zum Anziehen. Drückt er in dieser Zeit nicht auf den Arcade-Button, schaltet sich der Gerät von alleine ab.
Wenn man die normale Uhrzeit einstellen möchte, passiert das über die Weckzeit und einen langen(>5s Drücker auf den roten Knopf. Die Uhrzeit wird on release geschrieben, man kann also die aktuelle Uhrzeit+1 Minute einstellen und dann den Knopf solange gedrückt halten, bis eine genaue Uhr die nächste Minute anzeigt. Viele Arduino-Sketches zum Thema Uhr verwenden IDE um die Uhrzeit einzustellen, das finde ich ziemlich umständlich, ich will den Wecker ja nicht 2x im Jahr an den Rechner hängen müssen. Bis die EU mit der Zeitumstellung in die Pötte kommt, kann das sicher noch dauern. :roll:

Der Encoder ist zum Einstellen der Weckzeit wirklich toll, viel besser als diese "Knopf drücken und Halten-Lotterie". Musste wg eines Bugs die letzten Nächte die Weckzeit jedes Mal neu stellen und das geht selbst mit mächtig einen im T schnell und ohne den WAF zu beeinträchtigen. Der ist übrigens kristallgrün, sie meint, ich sollte in Serienfertigung gehen. 8-)

hier ist der aktuelle Code:

Code: Alles auswählen

//Lichtwecker v0.9

//todo:
//-> tm1637 bleibt. Separate Led für Alarmanzeige, wenn Zeit über. Soll auch gedimmt werden! (kompliziert?)

//
/*
 * 
 
 Kann: Wecken mit 16bit Sonnenaufgang, dazu MP3 Abspielen. Es gibt 4 Weckzustande: 
 1. Sonnenaufgang, random MP3 aus Ordner1 wird abgespielt. dauert 30 Minuten
 2. nach Ablauf von 1. ohne Interaktion kommt MP3 aus Ordner2 (lauteres, invasiveres Audio), 
    weiterhin volle Helligkeit
 3. Wenn während 1. oder 2. der Ausknopf (Arcade/Button2) gedrückt wird, wird ein angenehmer H-Wert gesetzt und angenehme leise Musik(Ordner3) gespielt.
 4. Wenn der Ausknopf erneut gedrückt wird, wird das Licht ausgefadet und abgeschaltet. 
    MP3 aus Ordner4 wird abgespielt (soll nur n kurzer Sound sein, unter 10s. Zu lang wird abgeschnitten wg ende des Fadeouts)

 Weckzeit wird angezeigt bei gedrücktem Ausknopf(Button2) Eingestellt mittels Encoder, 
 Druck auf Encoder Pushbutton (Button0) wechselt Stunden/Minuten. Was gerade eingestellt wird, 
 kann im Moment noch nicht angezeigt werden.(todo, benötigt andere library)

Neue Uhrzeit wird eingestellt indem man die Uhrzeit als Weckzeit einstellt und dann den Alarm an/aus 
Button (Button1) 5Sekunden oder länger hält. die Zeit wird on Button release geschrieben, man kann 
also die nächste Minute einstellen, den Knopf gedrückt halten und im richtigen Moment loslassen und 
hat eine sehr genau eingestellte Zeit.

Mit Doppelklick auf den Arcade Button (Button2) kann man die LED zu Beleuchtungszwecken verwenden, Über die Einstellung der Weckstunden 
kann man die Intensität einstellen. Stufe 0 ist besonders Dunkel, zur Verwendendung als Nachtlicht.

Die Helligkeit des Displays wird automatisch der Umgebungshelligkeit angepasst. Zum Einlesen wird ein 
Spannungsteiler zwischen 5v-Fotoresistor-5K_Ohm-GND Auf A7 eingelesen und nach Beschränkung auf 
setBrightness gemappt.
*/


//DFPlayer mini
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
SoftwareSerial mySerial(A2, A1); // RX, TX
DFPlayerMini_Fast myMP3;

#include "Wire.h"
#define DS3231_I2C_ADDRESS 0x68       //DS3231 Zeitgeber

//Tm1637
//#include <Arduino.h>
#include <TM1637Display.h>
// Module connection pins (Digital Pins)
#define CLK 5
#define DIO 4
TM1637Display display(CLK, DIO);

//Encoder
#include <Encoder.h>
Encoder myEnc(2, 3);

//Variablen
unsigned long schedulT = 0;     //Aufrufe der Uhrzeit /DS3231 begrenzen auf 1x pro Sekunde
unsigned long fadePointer = 0;  //wird bei Alarmauslösung gesetzt und dient zur Berechnung der Helligkeit während Alarmablaufs
unsigned long fadeZaehl=0;      //siehe fadePointer
unsigned long LpTimer = 0;      //erkennung von langen und kurzen Drückern auf button1 und button2. Kollision seeehr unwahrscheinlich

byte mins=0;           // Uhrzeit Minuten
byte hours=0;          // Uhrzeit Stunden 
byte Amins=0;          // Weckzeit Minuten
byte Ahours=0;         // Weckzeit Stunden
byte rAmins=0;         // Alarmbeginnzeit Minuten = 30 Minuten vor Weckzeit (r für real)
byte rAhours=0;        // Alarmbeginnzeit Stunden
byte BrighVal=1;       //Hwert Spwm

byte folder01 = 1;       //wieviele Tracks in folder#?
byte folder02 = 1;
byte folder03 = 1;
byte folder04 = 1;
byte playing = 0;               //(welches Verzeichnis)wird gerade abgespielt? 0=stop

byte AlarmState=0;            //Wird gerade geweckt? Wo sind wir in der Weckfunktion?
bool AlarmV=false;            //Weckfunktion an oder aus?
bool ZeigeAl=false;           //Weckzeit anzeigen wenn true
//unsigned long fadeVal=1;    //Helligkeitswert für analogWrite
unsigned long fadeULong=100;  //Var für die Logarithmische Berechnung des Helligkeitswertes 
                              //  (war mal int, jetzt mehr Auflösung)
                              
byte RestSchlaf=0;            //Restschlafanzeige während Alarm                          
bool zeigeRS=false;
//bool lichtAn=false;            //Var ist obsolet separate Leuchtfunktion an?

bool button2Pressed=false;    //für lange und kurze drücker brauchts ne extra Var
bool button1Pressed=false;    //für lange und kurze drücker brauchts ne extra Var
bool button0State = false;
bool button1State = false;
bool button2State = false;
byte encTarget=1;             //encoder target wechselbar für Stunden/Minuten/Leuchtstärke
unsigned long debTimer=0;     //Button debounce 

int encVal = 0;
int realBrigh=1;                //Durchschnittsbildung Helligkeitsmessung für Displayhelligkeit


//Eingänge
const byte button0Pin = 6;     // the number of the Encoder pushbutton pin
const byte button1Pin = 7;     // the number of other inputbuttons
const byte button2Pin = 13;    // s.o.
const byte HellPin = A7;       //Einlesen der aktuellen Helligkeit im Raum

//Ausgänge
const byte ledPin = 10;        //(10)LED Treiber angeschlossen an Pin9 oder 10 (16Bit PWM)
const byte disPin = 9;         //(9)Display Stromversorgung an armeleute DAC
//const byte DcdcEnPin = A3;     //Obsolet: DCDC Enable, HIGH=An
const byte AmpEnPin = 8;         //Amp Enable, High = Ausgeschaltet!



//------- nötig Zum Auslesen/schreiben von DS3231------------------------------------
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}
//--------------------------------------------------------------

//---------------------------------------------------------------
void setup(){

  
//Serial.begin(9600);

//I2C für DS3231  
  Wire.begin();
//Eingänge
  pinMode (button0Pin, INPUT_PULLUP);
  pinMode (button1Pin, INPUT_PULLUP);
  pinMode (button2Pin, INPUT_PULLUP);

  pinMode (2, INPUT_PULLUP);        //encoder
  pinMode (3, INPUT_PULLUP);
//Ausgänge
  pinMode (AmpEnPin, OUTPUT);
  //pinMode (DcdcEnPin, OUTPUT);
  digitalWrite(AmpEnPin,HIGH);  //Amp Aus 
  delay(10);
  //digitalWrite(DcdcEnPin,HIGH); //DCDC an, DFPlayer an
  //delay(600);
//serielle Verbindung zum DFplayer aufbauen
mySerial.begin(9600);
  myMP3.begin(mySerial);
  delay(400);
  myMP3.volume(17);         //Lautstärke
  delay(10);
  myMP3.EQSelect(3);
  delay(10);
 //byte volume = myMP3.currentVolume();
 
  folder01 = myMP3.numTracksInFolder(01);
    delay(10);
  folder02 = myMP3.numTracksInFolder(02);
    delay(10);
  folder03 = myMP3.numTracksInFolder(03);
    delay(10);
  folder04 = myMP3.numTracksInFolder(04);
    delay(10);
  
  /*Serial.print("folder01 count");
  Serial.println(folder01);
  Serial.print("folder02 count");
  Serial.println(folder02);
  Serial.print("folder03 count");
  Serial.println(folder03);
  Serial.print("folder04 count");
  Serial.println(folder04);
  
  Serial.println("starting...");  
  
  delay(10);                        //Erkennung der Ordner, Anzahl Dateien */
  
  //myMP3.sleep();                    //Audio aus @Startup
  
 //digitalWrite(DcdcEnPin, LOW);                    //Stromsparen
  


//TM1637 
display.setBrightness(2);

//16bit PWM
setupPWM16();                     //init
analogWrite16(ledPin, 0);         //PWM an LED=0
analogWrite16(disPin, 55534);     //PWM_DAC an Display=50000



  while(digitalRead(button2Pin)==HIGH){
    display.showNumberDec(millis(), true);
    }
  if (digitalRead(button2Pin)==LOW){
    randomSeed(millis());
    }
}

//---------------------------------------------
void setupPWM16() {
  DDRB  |= _BV(PB1) | _BV(PB2);       /* set pins as outputs */
  TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                 /* mode 14: fast PWM, TOP=ICR1 */
  TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                  /* prescaler 1 */
  ICR1 = 65535;                         /* TOP counter value (freeing OCR1A*/
}

//---------------------------------------------

//------------------------------
void analogWrite16(uint8_t pin, uint16_t val)
{
  switch (pin) {
    case  9: OCR1A = val; break;
    case 10: OCR1B = val; break;
  }
}

//-Helligkeitsanpassung Display-----------------------------------
void setBrigh(){
                                                     //wenn kein Alarm, Helligkeit anpassen 
  const int heller=1200;                             //Offset für Korrektur der Helligkeit, falls Anzeige zu dunkel.
  int readBrigh = analogRead(HellPin);               //Mittelwertbildung für Sprungfreiheit, wichtig wen Licht an ist (PWM-> springende Werte an/aus)
      readBrigh=constrain(readBrigh, 0, 600);
      realBrigh=(80*realBrigh+readBrigh)/81;
  byte writeBrigh = map(realBrigh, 0, 600, 1,4);              //Dimminfunktion _im_ Display, ist nötig, da sonst der Strom bei wenig licht zu groß ist und Display flimmert.
  unsigned int disDAC= map(readBrigh, 0, 600, 34000, 62535);  //Wenn unterer Wert zu niedrig  geht nix oder flimmerts, bei Vollgas ist die Stromaufnahme zu hoch für den PWM-Pin(?)
  /*
  Serial.print("Einlesewert:  ");
  Serial.println(readBrigh);
  Serial.print("Wert Display: ");
  Serial.println(writeBrigh);
  Serial.print("Wert PWM:     ");
  Serial.println(disDAC);*/
  
  display.setBrightness(writeBrigh);        //1-4
   

   if(readBrigh>80){                                  //unterhalb 80 ist das Licht aus-> dunkles Display
    analogWrite16(disPin, constrain((disDAC+heller), 34000, 62535));           //65535 PWM_DAC an Display
   }
    if(readBrigh<81){
  analogWrite16(disPin, disDAC);           //65535 PWM_DAC an Display
    }
  
}
//---^setbrigh^----




//---dispTm1637 Anzeigefunktion----------------
void dispTm1637(byte Verbleib){
// Create time format to display:
int displaytime = 0;

if (Verbleib<1){
      if(ZeigeAl==false && RestSchlaf<1){
        displaytime=(hours * 100) + mins;       //aktuelle Uhrzeit anzeigen
         }      
      if(ZeigeAl==true ){         
      displaytime=(Ahours * 100) + Amins;     // Alarmzeit anzeigen/einstellen      
        }
  }
      
if (RestSchlaf>0){      //verbleibende Zeit im Sonnenaufgang
  displaytime=Verbleib; 
}



//Alarm An/Aus wird über den Doppelpunkt angezeigt 

if (AlarmV){
  display.showNumberDecEx(displaytime, 0b11100000, !Verbleib);    //Wenn Alarm an, Doppelpunkt an. (...~decEx) die Lib nervt hart, das Display nervt, der Chip auch D:
  }                                                               //shownumberDecEx(AnzuzeigendeZahl, DoppelpunktAn, padding/w Zeroes)
                                                                  //zero-Padding soll während Anzeige von Restschlaf aus sein wg Verwechslungsgefahr mit aktueller Uhrzeit
if (!AlarmV){                                                     //deswegen !Verbleib
  display.showNumberDec(displaytime, !Verbleib);                  //wenn Alarm aus, Doppelpunkt aus.
  }  
    
}
//------------- /tm1637--------------------------

//-----audioPlbk()-------------
void audioPlbk(byte what){

//wenn gespielt wird, ales an ist und jetzt ausgeschaltet werden soll 
  if (playing && !what){
           
      //myMP3.volume(0);                                              //PopKlickVerhinderungsversuch
      //delay(10);
      //Serial.println("Amp ausschalten");
      digitalWrite(AmpEnPin, HIGH);                                //AMP ausschalten(HIGH=aus!)
      //delay(20);
      //Serial.println("DCDC ausschalten");
      //digitalWrite(DcdcEnPin, LOW);                                //DCDC aus
      delay(10);
      //digitalWrite(DcdcEnPin, LOW);
     }

//Wenn nicht gespielt wird und daher alles aus ist, anschalten     
   if(!playing && what){
      //Serial.println("DCDC Anschalten");   
      //digitalWrite(DcdcEnPin, HIGH);                                //DCDC an
      //delay(1000);
      //Serial.println("Volume 0");
      myMP3.volume(0);
      delay(20);
      //Serial.println("Amp Anschalten"); 
      digitalWrite(AmpEnPin, LOW);                                //AMP anschalten(HIGH=aus!)
      //Serial.println("Volume 17");
      myMP3.volume(17);                                          //Lautstärke einstellen wg powercycle 
     }

//Was soll gespielt werden?     
  if (playing!=what){
    
    if (what==1){         
                                                       
      myMP3.playFolder(what, random(1,folder01));
            }
           
    if (what==2){
      myMP3.playFolder(what, random(1,folder02));
      }
      
    if (what==3){
      myMP3.volume(14);                               //etwas leiser als Alarm, ist Musik und daher höherer RMS als Ambient.
      delay(10);
      myMP3.playFolder(what, random(1,folder03));
      }
      
     if (what==4){
      myMP3.playFolder(what, random(1,folder04));
      } 
      
    //Serial.print("Spiele folder ");
    //Serial.println(what);  
    playing=what;   
    }
  
  
  }
//-----^audioPlbk()^-------------



void encoder() {
  
  int newPosition = (myEnc.read())/4 ;      //Liest pro Rastung 4 neue Werte ein, daher /4
  if (newPosition != encVal) {
      encVal = newPosition;   
      }
}
//----^encoder^--------------------------------------

//---Knöpfe einlesen-----------------------------------------
void buttonLogic(){

  button0State = digitalRead(button0Pin);
  button1State = digitalRead(button1Pin);
  button2State = digitalRead(button2Pin);
  if(AlarmState==false&&(button2State==LOW||encTarget==2)){
    encoder();
  }

  //Zeiteinstellung. Wenn encTarget=2 ist Licht an, dann wird über Encoder die Helligkeit eingegeben
  if(debTimer < millis() && button0State==LOW && encTarget!=2){             //Button0 selects encoder Target to adjust minutes and hours
        
        if (encTarget==0 && debTimer<millis()){
        debTimer=(millis()+300);
        encTarget=!encTarget;
        encVal=Amins;
        myEnc.write(Amins*4);
        }
        if (encTarget==1&& debTimer<millis()){
        debTimer=(millis()+300);  
        encTarget=!encTarget;
        encVal=Ahours;
        myEnc.write(Ahours*4);
        }
    }
  
  if (encTarget==0 && AlarmV==false && ZeigeAl){           //Stunden für Alarm setzen, Überlauf bei 23 Stunden bzw 59 Minuten 
    if (encVal>23){                             //wenn Alarm=aktiv ist verstellen unmöglich               
      encVal=0;                                 //Bug: Encoder wird weiter eingelesen, bei der 
      myEnc.write(encVal*4);                    //nächsten Anzeige von Weckzeit wird der neue 
      }                                         //Encoderwert geschrieben, kann zu wertesprüngen führen. >LowPrio
    if (encVal<0){
      encVal=23;
      myEnc.write(encVal*4); 
      }
    Ahours=encVal;
  }
  
  if (encTarget==1&& AlarmV==false && ZeigeAl){              //Minuten, Überlauf
    if (encVal>59){
      Ahours=Ahours+1;
      encVal=0;
      myEnc.write(encVal*4);
      }
    if (encVal<0){
      Ahours=Ahours-1;
      encVal=59;
      myEnc.write(encVal*4);
      }
    Amins=encVal;
  }
  
//Variablenabgleich nach möglicher Veränderung von encVal, Amins/AHours sollen gleich bleiben und nicht springen

  if(encTarget==1 && Amins!=encVal && !ZeigeAl){            //encTarget zeigt auf Minuten, soll also diesen Wert haben
    encVal=Amins;
    myEnc.write(encVal*4);
    }
  if(encTarget==0 && Ahours!=encVal && !ZeigeAl){           //encTarget zeigt auf Stunden, soll also diesen Wert haben
    encVal=Ahours;
    myEnc.write(encVal*4);  
    }

//bei Licht an, gehen die Werte bis 48, kein Überlauf zu 0/48 wg Blendwirkung von 0->48, dunkel nach hell, stattdessen Wert halten
  if (encTarget==2){
  if (encVal>48){                                         
      encVal=48;                              
      myEnc.write(encVal*4);                  
      }                                      
    if (encVal<0){
      encVal=0;
      myEnc.write(encVal*4); 
      }
    Leucht(encVal);  
  }


//--Erkennung von kurzen/langen Drückern auf button1--
if(button1State==LOW && button1Pressed==false ){        //press-Event 
     LpTimer=millis();  
     button1Pressed=true;
    }
    
if(button1State==HIGH && button1Pressed==true){        //release-Event 
     
     //kurzer Knopfdruck = umschalten Alarm an/aus
     if(millis()-LpTimer>10 && millis()-LpTimer<5000){ 
        
        AlarmV=!AlarmV;                                //Wenn Alarm an, Alarm= aus
        
        if(AlarmV){             //Wenn Alarm an, rAmins&rAhours schreiben für früheren Sonnenaufgangsbeginn                                
                   
          if (Amins>29){
          rAmins = Amins-30;                //Umrechnung der 30 Minuten Vorlauf auf Stunden
          rAhours = Ahours;
          }
          if (Amins<29 &&Ahours>0){
          rAmins = Amins+30;
          rAhours = Ahours-1;
          }
          if (Amins>29 && Ahours==0){
          rAmins = Amins+30;
          rAhours = 23;
          }
          
        
                              
        }
        button1Pressed=false;         
      }


     //langer Knopfdruck = Uhrzeit setzen
     if(millis()-LpTimer>4999&&AlarmV==false){      
        
        setDS3231time();                            // schreibt aktuelle Amins&AHours in DS3231
        button1Pressed=false; 
        }     
    }
//Button2 (Arcade) Einfach- und Doppelklick
if(button2State==LOW && button2Pressed==false&&LpTimer<millis()&&debTimer<millis()){        //press-Event 
    button2Pressed=true;
    debTimer=millis()+30;
    }
if(button2State==HIGH && button2Pressed==true&&LpTimer<millis()&&debTimer<millis() ){        //release-Event 
    button2Pressed=false;
    LpTimer=millis()+250;
    debTimer=millis()+30;
  }
if(button2State==LOW && button2Pressed==false&&LpTimer>millis()&&debTimer<millis()){        //doubleklick-Event 
    button2Pressed=false;
    
    if(encTarget!=2){                  //Licht wird angeschatet, Startwert Licht ist 24 von 48 Stufen
     encTarget=2;
     encVal=24;
     myEnc.write(encVal*4);
     //fadePointer=millis();             //für automatisches Licht-Abschalten
     goto skip1; 
      }
      
    if(encTarget==2){                  //Licht wird ausgeschaltet, Encoder braucht die Werte aus Zeiteinstellung (es wird zu den Minuten gesprungen) 
     encVal=Amins;
     myEnc.write(encVal*4); 
     encTarget=1; 
     analogWrite16(ledPin, 0);        //Leucht() kann nur licht anschalten, aber nicht aus, deswegen hier
      }
    
    skip1: 
    debTimer=millis()+600;
    }


  

//Alarmzeit soll angezeigt werden
 if(button2State==LOW && AlarmState == 0){           
        ZeigeAl=true;                                //notwendig auch zum Einstellen des Alarms u. der Zeit    
    }
    
 if (button2State==HIGH){
    ZeigeAl=false;  
    }

    
//Alarmlogik Button 2 (Arcade Button)   
 if(button2State == LOW &&AlarmState == 1 && debTimer < millis()){      //Weckphase 1 läuft, Schläfer ist wach-> Sprung zu 3
   debTimer=(millis()+900);   
   fadePointer=millis();                                                //Zähler reset für einfacheres Debugging
   fadeZaehl=0;
   AlarmState=3;
    }
 if(button2State == LOW && AlarmState == 2 && debTimer < millis()){      //WeckPhase 2(lauter als1) läuft, Schläfer ist wach-> Sprung zu 3
  debTimer = (millis()+900);
  fadePointer = millis();                                               //Zähler reset für einfacheres Debugging
  fadeZaehl = 0;
  AlarmState = 3;
    }
 if(button2State == LOW && AlarmState == 3 && debTimer < millis()){      //Schläfer steht auf und verlässt das Bett
  debTimer = (millis()+900);
  fadePointer = millis();
  fadeZaehl = 0;
  AlarmState = 4;                                                       
    }
 if(button2State == LOW && AlarmState == 4 && debTimer < millis()){      //Schläfer will das Licht schnell aus haben
  debTimer = (millis()+900);
  fadeULong = 100;                                                      //Alarm() A-state=4 macht den Rest
   }
}//close ButtonLogic

//-----------------------------------------------------------------
void setDS3231time()
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(00)); // set seconds
  Wire.write(decToBcd(Amins)); // set minutes
  Wire.write(decToBcd(Ahours)); // set hours
  //Wire.write(decToBcd(1)); // set day of week (1=Sunday, 7=Saturday)
  //Wire.write(decToBcd(1)); // set date (1 to 31)
  //Wire.write(decToBcd(1)); // set month
  //Wire.write(decToBcd(0)); // set year (0 to 99)
  Wire.endTransmission();
}
//----------------------------------------------------------------------

void readDS3231time()
    {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);                                // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  byte second = bcdToDec(Wire.read() & 0x7f);   //wird gelesen, aber nicht gebraucht. 
  mins = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
  //*dayOfWeek = bcdToDec(Wire.read());
  //*dayOfMonth = bcdToDec(Wire.read());
  //*month = bcdToDec(Wire.read());
  //*year = bcdToDec(Wire.read());
}


//--------------------------------------------------
void alarm(){
  //Sonnenaufgang: nach #Zeitintervall wird der bisherige Helligkeitswert mit 1,01 multipliziert. 
  //~22Minuten Fade
  //MP3Start nach 12Minuten, random Track in Folder 01, Soundscapes sind mit Fade-In überlagert, Lautstärkekontrolle am dfPlayer Mini hat nur 30 Stufen (reicht nicht)
  
  
  
const int upSpeed=1000;                   //(1000neu=>20+x(?) Minuten, 100=>127sek.) sets Interval for multiplication of fadeULong(fade in)
                                          //kleinere Werte=schnellerer Fade 
const int dnSpeed=6;                      //(6) sets Interval for division of fadeULong (fade out)
const int lowMinim=100;                   // Offset für Einstellung der niedrigsten Helligkeitsstufe, damit der langsame Teil des Fades auch im nützlichen Helligkeitsbereich ist
                                          //sollte nicht über 800 sein, sonst wird die auskommentierte constrain funktion in Sonnenaufgang notwendig  
unsigned long fadeTime=(millis()-fadePointer);  //wie lange läuft der Alarm schon? FadePointer bekommt von Buttonlogic() Reset bei Sprüngen in A-states 3+4
unsigned int fadeVal=1;                   //local Var für Ausgabe an 16Bit PWM



//Alarm Stufe 1: Sonnenaufgang, Gezwitscher
  if (AlarmState==1 && fadeTime < 1200000){
    
    //Start Audio wenn FadeTime>x
    if(fadeTime>900000 && playing==0){                      //15min nach Beginn(900K Millisekunden)                                              
      
       audioPlbk(1);                                       //spielt Ordner 1 (Vogelgezwitscher, Ambient Sounds)  
       }

    
 //pre-sunrise linearer fade-in für niedrige Helligkeit
    if(fadeTime<100){                           //nur 1x am Anfang ausführen
          for (int i = 0; i <= lowMinim; i++) {
              analogWrite16(ledPin, i);
              //Serial.print("1schnell= ");
              //Serial.println(i); 
              delay(30);
              }
    }
    

      //Sonnenaufgang Berechnung  
    if ((fadeZaehl+upSpeed)<fadeTime  &&fadeULong<4901480){   //Fade-Funktion wird ausgeführt, solange keine 5M erreicht werden können
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong*1.01);                               //
      fadeVal=fadeULong/76.3+lowMinim;                          //5000000/76,3= 65535, der 100%wert für PWM16, map() macht mist bei Werten größer als int
      //fadeVal=constrain(fadeVal,1,65535);
      analogWrite16(ledPin, fadeVal);
      //Serial.print("1langsam= ");
      //Serial.println(fadeVal); 
        }
        
  }
      
  // Maximum, ist erreicht(1800000ms/30 Minuten preset)       //Nach T>30 Minuten weiter bei AlarmState2,
 if(AlarmState==1 &&fadeTime>1800000){
        AlarmState=2;
      }
   


//Alarm Stufe 2: Vollgas Licht, laute Weck-Musik 
  if(AlarmState==2){                                        //schläft noch&& bisherige Maßnahmen zeigten keine Wirkung
     fadeULong=5000000;                                     //maximum Warp
      fadeVal=65535;
      analogWrite16(ledPin, fadeVal);
     if(playing!=2){
       audioPlbk(2);                                        //spielt Ordner 2 (laute Weckroutine)
        }
        
        //renitenter Schläfer braucht nach 35Min noch härtere Gangart
     if (fadeTime>2100000){
        playing=1;          //sonst nimmt audioPlbk() keine Befehle an
        audioPlbk(2);       //<-Track erstellen und neues Verzeichnis reinkopieren, nummer hierhin                  //35minuten Neuer, aggressiver Wecktrack, volle Lautstärkespielt laute Weckroutine  
       }
       
     if (fadeTime>2400000){                            //40 Minuten. genug geweckt, ist keiner Da-> ausschalten!             
      AlarmState=4;                                     //AlarmState=4 macht das Licht aus
      AlarmV=false;
      }
    
  }//close AState2


//Alarm Stufe3:  ist wach, braucht aber noch Licht, Fadeval auf angenehmen Wert 
  if(AlarmState==3){                                    
    if(playing!=3||playing==1){
       audioPlbk(3);                                    //Spielt Ordner 3 (angenehme leise Musik)
        }
       
       if(fadeULong>2000000){                           //angenehm hell, mit zwischenFadeout
        
            for (unsigned long i = fadeULong; i >= 2000000; i=i*0.99) { //Zwischenfade
              fadeVal=(i/76.3);
              analogWrite16(ledPin, fadeVal);
              //Serial.print("3zwischenF= ");
              //Serial.println(i);
              delay(10);
              }
          fadeULong=2000000;
          }  
        if(fadeULong<2000000){                           //angenehm hell, mit zwischenFadein
        
            for (unsigned long i = fadeULong; i <= 2000000; i=i*1.01) { //Zwischenfade
              fadeVal=(i/76.3)+lowMinim;
              analogWrite16(ledPin, fadeVal);
              //Serial.print("3zwischenF= ");
              //Serial.println(i);
              delay(10);
              }
            fadeULong=2000000;
          }     
      fadeVal=(fadeULong/76.3);                         //Wert wird gehalten
      analogWrite16(ledPin, fadeVal);      
       //Serial.print("3hold= ");
       //Serial.println(fadeVal);

       
      if (fadeTime>600000){                //ist Wach, hat Lichtausmachen vergessen(10 Minuten nach ButtonPress)
          AlarmState=4;  
          }
   }//close Astate3

 //Alarm Stufe 4: Es wird aufgestanden, Licht kann ausfaden, kleiner Soundschnipsel als Bestätigung 
  if(AlarmState==4&&fadeULong>100){                         //steht jetzt auf Licht soll ausfaden
    if(playing!=4){
       audioPlbk(4);                                     //Spielt Ordner 4 (kurzer Bestätigungssound, +1Up Mario)
       }
       
    if ((fadeZaehl+dnSpeed)<fadeTime){
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong/1.01);  
      fadeVal=(fadeULong/76.3);
      analogWrite16(ledPin, fadeVal);
      //Serial.print("4 out= ");
      //Serial.println(fadeVal);     
      }    
  }

  
  if(AlarmState==4&&fadeULong<101){                          //Fade ist durch, alles zurück auf Ausgangswerte/abschalten
   fadeULong=100;
   fadeZaehl=0;
   AlarmState=0; 
   audioPlbk(0);
   AlarmV=false;
   analogWrite16(ledPin, 0);
      } 
  
    
}
//----------^void alarm()^-------------------------------


//Lichtfunktion, bekommt Werte von Buttonlogic()/encoder und macht daraus Helligkeit
void Leucht(byte intens){
  unsigned int leuchtVal= 10;
  for (byte i = 0; i <=intens; i++) {       //logarithmische Berechnung der Helligkeit
    leuchtVal=leuchtVal*1.2;
     }
  if(leuchtVal<13){     //minimal-Licht zur Verwendung als Nachtlicht
      leuchtVal=8;
     }
  analogWrite16(ledPin, leuchtVal);
  //Serial.print("leuchtVal: " );
  //Serial.println(leuchtVal);

//automatische Lichtabschaltung nach 2 Stunden, nur aktiv bei intens>0, 0=Schlaflicht  

 /* if(intens && (fadePointer-millis())>7200000){
    encTarget=1;
    encVal=Amins;
    myEnc.write(encVal*4);
    analogWrite16(ledPin, 0);
    }*/
}
//-------^void Leucht()^----------------------------------------------------------------------------------------



void loop(){
 
  if (schedulT<millis()){                                      //Aufrufe begrenzen
  
      readDS3231time();                                        //Zeit einlesen 
      setBrigh();                                    //Helligkeit im Display an Umgebung anpassen, bei AlarmState>0 max Brightness 

      //Restschlafanzeige
    if (AlarmState){
        zeigeRS=!zeigeRS;                           // Abwechselnd Uhrzeit + Restschlaf anzeigen
        if (zeigeRS&&AlarmState==1){
              RestSchlaf=(1800000-(millis()-fadePointer))/60000; //Weckroutine dauert 30 Minuten
            }
        if (zeigeRS&&AlarmState==2){                             //A-State=2 Heißt Zeit ist abgelaufen
              RestSchlaf=0;
            }  
        if (zeigeRS&&AlarmState==3){
              RestSchlaf=(600000-(millis()-fadePointer))/60000;   //hat 10 Minuten Zeit zum Anziehen
            }       
        if (!zeigeRS){
              RestSchlaf=0;
            }         
        }
      
     if (!AlarmState && encTarget!=2){  //Nur Uhrzeit wenn nicht benötigt
        zeigeRS=false;
        RestSchlaf=0;
          }

    //Zeit für Alarm?
        if(AlarmV==true &&AlarmState==0 && hours==rAhours && mins==rAmins){      //Bedingung für Alarmauslösung 
              fadePointer=millis();                                                  //Pointer setzen für Zeitablauf
              //lichtAn=false;
              encTarget=1;            //Licht ausschalten, falls an
              AlarmState=1;        
          }

    /*Beleuchtung
        if(lichtAn){
       Leucht(Ahours);
      }*/
          
     

     /*Serial.print("AlarmState: " );
     Serial.println(AlarmState);
     Serial.print("fadeULong= ");
     Serial.println(fadeULong);     */      

    // 1x pro Sekunde  
     schedulT= millis()+1000;                                   
   }//scheduler zumachen
   
 //Solange der Alarm läuft, wird die zugehörige Funktion auch aufgerufen  
 if(AlarmState){
    alarm();
    }



//Knöpfe einlesen
 buttonLogic();                                               

// display auf dem tm1637   
 dispTm1637(RestSchlaf);                                                
 
                                                // display auf dem LCD
}
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Ich habe einiges weitergefrickelt hier. Für den zweiten Wecker, den ja meine Schwester bekommt hab ich mir Stereosound vorgenommen, der soll von einem PAM 8403 verstärkt werden.

Ich habe mich versucht im Aufbau schönerer Platinen.
wecker5kl01.jpg
Dann kam heraus, dass der dfPlayer ne kleine Drecksau ist, und Entstörungsmaßnahmen braucht. Obligatorisch ist die spannungreduzierte Versorgung @4,2V und die serielle Verbindung mit 1k inline für RX UND TX. Im Netz findet man Anleitungen, dass nur in einem Pfad(vergessen welcher) der Widerstand nötig wäre, aber das führt zu deutlich hörbaren Einstreuungen. Dann sollte es ja eigentlich reichen, die DAC-Ausgänge des dfPlayers über nen Spannungsteiler am Verstärker anzuschließen, nur leider wird da das Vergnügen durch ein brummähnliches Geräusch gestört. Nach etwas googeln hab ich ein Datenblatt zum yx5200 genannten Chip gefunden, und siehe da, es gibt einen Pin, der für "decoupling" verwendet werden soll. Da hab ich mal nen Elko mit 100µF drangehängt und mit dem Kopfhörer als einzige am DAC angeschlossene Last gekuckt, wie sich das verhält. Wenn ich den Elko direkt an der Masse vom Kopfhörer kontaktiert hab, ging das Geräusch weg, bei anderen Massepunkten nicht. Gleiches Prinzip am PAM8403 probiert: funzt. :)
Durch Labornetzteil-Fehlbedienung hab ich ne µSD-Karte gehimmelt und war am grübeln, ob das an meiner Entkopplungslösung gelegen haben könnte, die Neue zeigt aber keinerlei Ausfallserscheinungen, hoffentlich bleibt das so. :?

Da ich den integrierten Amp nicht nutze, hab ich den direkt von der Platine runtergelötet und einen der dadurch freiwerdenden Pins für die Entkopplungsleitung zum Verstärker genutzt. Hier ist der aktuelle Schaltplan:
Lichtwecker Schaltplan03.jpg
Der Spannungsteiler am Helligkeitssensor verträgt auch mehr als 20k, der Sensor, den ich da jetzt habe kommt von Maxens und hat ne etwas andere Kennlinie als der im ersten Wecker. Den höheren Vorwiderstand für die LED hab ich durch leicht unterdimensionierte Zuleitung erreicht, die lässt sich auch leichter verlegen. 8-)
Der Spannungsteiler vor den Eingängen am Verstärker verträgt wahrscheinlich auch 8-10k statt der 4,7. Mir ist das laut genug und man bekommt ne bessere SNR-Ratio, weil man den DAC voll(er) aussteuert. Die Platine die ich hier hab führt den Enable-Pin nicht heraus und hat dafür 2x GND, das fand ich unnötig und hab einen der beiden GND-Pins umgewidmet. Leider macht Pam Knackser, wenn sie über enable an- und ausgeschaltet wird, da hatte ich mir besseres erhofft.

kleine Anprobe, wird mal wieder knapp. Der Entkopplungselko kommt beim nächsten Mal auf die Lochrasterplatine, die Heißkleber-Huckepack Lösung ist nicht so schön...
wecker5kl03.jpg
So sieht das jetzt aus. Die Verdunklungsfolie auf dem Display verbessert die Lesbarkeit enorm, weil man die inaktiven Segmente quasi nicht mehr sieht. Vorher war da der LED-Phosphor zu sehen, der natürlich auch auf blaues Licht aus anderen Quellen anspricht.
wecker5kl06.jpg
Bei Gehäuse n°2 hab ich es geschafft, nicht bis ganz hinten durchzufräsen, der KüKö hat dadurch bessere Konvektion. Beim Bauhaus hatten sie wg Corona nurnoch Reststücke an Plexiglas, das größte hatte 6mm, nehm ich halt das. Ist jetzt auch ne andere Mattierungsfolie ohne Streifen und mit recht grober Textur. Gefällt mir besser.
wecker5kl05.jpg
wecker5kl07.jpg
Am Code gings auch weiter, ich hatte zwischenzeitlich mit ner race-condition eine Beschleunigungsfunktion für den Encoder zusammengepfuscht, die bei schnellem Drehen statt Minuten halbe Stunden addiert/subtrahiert hat. Das war dann aber doch etwas fummelig und wurde wieder auf die Umschaltlösung zurückgebaut. Hab mal Quellen angegeben und hier und da etwas aufgeräumt. Die v1.0 kommt nah, ich muss mich zusammenreißen nicht neue features zu proggen, während noch ungeliebter Kleinkruscht abgearbeitet werden will.

Code: Alles auswählen

Edit: Aktualisierter Code vom Post drunter

//Lichtwecker v1.0
//Dieser Sketch ist getestet auf Nano V3 (ATmega 168:Button2Pin@#13, nur <80 Plätze für shuffle wg wenig Speicherplatz; Bedarf wahrscheinlich weiterer Optimierungen
//                                        AtMega 328T, Button2Pin@#12, Kann 255 Plätze für shuffle, Displayhelligkeit anderer Sensor und Spannungsteiler)


//done: Encoderbeschleunigung wieder rauswerfen, ist doof wg Undosierbarkeit.
//Done! beleuchteter Taster für Anzeige Alarm Scharfgestellt, LED soll auch gedimmt werden. Ungenutzten tm1637 Pin rausführen?
//done: Shuffle anstelle von Random für Songs abspielen. (Speicherbedarf! Ò_ó)
//done: neue Funktion für Fading: averaging vor Ausgabe an PWM, ersetzt jetzt nicht mehr notwendige for-loops.
//done: einstellbarer Sleep-Timer für Leselicht, nach x Minuten schaltet die Lampe von alleine ab.



//todo:
//
//neues Feature: Sleep Timer bekommt nach Wahl einen langsamen "Sonnenuntergang in den letzten x Minuten. 
//                ^Mangel an Bedienelementen?^





//
/* Beschreibung:
 * 
 
 Kann: Wecken mit 16bit Sonnenaufgang, dazu MP3 Abspielen(no1). Es gibt 4 Weckzustande: 
 1. Sonnenaufgang, MP3 aus Ordner1 wird abgespielt. dauert 30 Minuten
 2. nach Ablauf von 1. ohne Interaktion kommt MP3 aus Ordner2 (lauteres, invasiveres Audio), 
    weiterhin volle Helligkeit
 3. Wenn während 1. oder 2. der Ausknopf (Button2) gedrückt wird, wird ein angenehmer H-Wert gesetzt und angenehme leise Musik(Ordner3) gespielt.
 4. Wenn der Ausknopf erneut gedrückt wird, wird das Licht ausgefadet und abgeschaltet. 
    MP3 aus Ordner4 wird abgespielt (soll nur n kurzer Sound sein, unter 10s. Zu lang wird abgeschnitten wg ende des Fadeouts)

 Weckzeit wird angezeigt bei gedrücktem Ausknopf(Button2) Eingestellt mittels Encoder, 
 Druck auf Encoder Pushbutton (Button0) wechselt Stunden/Minuten. Was gerade eingestellt wird, 
 kann im Moment noch nicht angezeigt werden.(todo, benötigt andere library)

Neue Uhrzeit wird eingestellt indem man die Uhrzeit als Weckzeit einstellt und dann den Alarm an/aus 
Button (Button1) 5Sekunden oder länger hält. die Zeit wird on Button release geschrieben, man kann 
also die nächste Minute einstellen, den Knopf gedrückt halten und im richtigen Moment loslassen und 
hat eine sehr genau eingestellte Zeit.

Mit Doppelklick auf den Arcade Button (Button2) kann man die LED zu Beleuchtungszwecken verwenden, Über die Einstellung der Weckstunden 
kann man die Intensität einstellen. Stufe 0 ist besonders Dunkel, zur Verwendendung als Nachtlicht.

Die Helligkeit des Displays wird automatisch der Umgebungshelligkeit angepasst. Zum Einlesen wird ein 
Spannungsteiler zwischen 5v-Fotoresistor-20K_Ohm-GND Auf A7 eingelesen und nach Beschränkung auf 
setBrightness gemappt.

Folgende Bibliotheken werden benötigt:
bereits in Arduino IDE integriert:
-wire.h
-SoftwareSerial.h 

Separat zu installieren:
-DFPlayerMini_Fast by PowerBroker2
-TM1637 by Avishay Orpaz
-encoder.h  by Paul Stoffregen

Die Codeschnipsel um den ds3231 einzulesen stammen von: 
https://tronixstuff.com/2014/12/01/tutorial-using-ds1307-and-ds3231-real-time-clock-modules-with-arduino/

Die 16Bit-PWM Codeschnipsel stammen von:
https://github.com/RoboUlbricht/arduinoslovakia/blob/master/timer/timer1_pwm_16bit/timer1_pwm_16bit.ino

getestet auf: Arduino Nano v3 (AtMega168, AtMega328P) (bei 168 ist der Speicherplatz ist knapp, serial-> PC könnte Probleme verursachen, wenn es aktiviert wird)

*/


//DFPlayer mini
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
SoftwareSerial mySerial(A2, A1); // RX, TX
DFPlayerMini_Fast myMP3;

#include "Wire.h"
#define DS3231_I2C_ADDRESS 0x68       //DS3231 Zeitgeber

//Tm1637
//#include <Arduino.h>
#include <TM1637Display.h>
// Module connection pins (Digital Pins)
#define CLK 5
#define DIO 4
TM1637Display display(CLK, DIO);

//Encoder
#include <Encoder.h>
Encoder myEnc(2, 3);

//Eingänge
#define button0Pin 6      // Encoder pushbutton pin
#define button1Pin 7      // Alarm an/aus & Zeit setzen Knopf
byte button2Pin = 12;     // Arcade Button auf der Oberseite - Neue Rev:D12 alte Revision: Pin D13
const byte HellPin = A7;        //Einlesen der aktuellen Helligkeit im Raum über Fotowiderstand*Spannungsteiler

//Ausgänge
const byte ledPin = 10;         //(10)LED/Mosfet - Treiber angeschlossen an Pin9 oder 10 (16Bit PWM)
const byte disPin = 9;          //(9)Display Stromversorgung an armeleute DAC//Spannung soll auch für Tasterbeleuchtung in V2 herhalten!
const byte AmpEnPin = 8;        //Amp Enable, brauchts nicht mehr
const byte PampEnPin = A3;      //ist A3, verschoben wg debug dfPlayer

//Variablen
unsigned long schedulT = 0;     //Aufrufe der Uhrzeit /DS3231 begrenzen auf 1x pro Sekunde
unsigned long fadePointer = 0;  //wird bei Alarmauslösung gesetzt und dient zur Berechnung der Helligkeit während Alarmablaufs
unsigned long fadeZaehl=0;      //siehe fadePointer
unsigned long LpTimer = 0;      //Erkennung von langen und kurzen Drückern auf button1. 
unsigned long DCTimer = 0;     //Erkennung von Doppelklick auf Button2.

unsigned long LedPWMVal =0;     //Smoothing und Ausgabe Der LEd-PWM
unsigned int leuchtVal1= 0;     //für Übertragung des PWM-Werts zwischen Berechnung und Smoothing/Ausgabe
int smooBrigh=400;              //Helligkeitssensor braucht smoothing, da die PWM der LED (wenn an) stark 
                                //unterschiedliche Helligkeiten erzeugt. (besser wäre kleiner Elko)                             
byte mins=0;           // Uhrzeit Minuten
byte hours=0;          // Uhrzeit Stunden 
byte Amins=0;          // Anzeige-Weckzeit Minuten
byte Ahours=0;         // Anzeige-Weckzeit Stunden
byte rAmins=0;         // Alarmbeginnzeit Minuten = 30 Minuten vor Weckzeit (r für real)
byte rAhours=0;        // Alarmbeginnzeit Stunden

byte folder01 = 1;       //wieviele Tracks in folder#?
byte folder02 = 1;
byte folder03 = 1;
byte folder04 = 1;
byte playing = 0;             //(welches Verzeichnis)wird gerade abgespielt? 0=stop

byte AlarmState=0;            //Wird gerade geweckt? Wo sind wir in der Weckfunktion? (0-4)
bool AlarmV=false;            //Weckfunktion an oder aus?
bool ZeigeAl=false;           //Weckzeit anzeigen wenn true
bool lichtAn=false;           //Variable für Lichtfunktion

unsigned long fadeULong=100;  //Var für die Logarithmische Berechnung des Helligkeitswertes 
                                                            
byte RestSchlaf=0;            //Restschlafanzeige während Alarm                          
bool zeigeRS=false;

bool button2Pressed=false;    //für lange und kurze drücker brauchts ne extra Var
bool button1Pressed=false;    //für lange und kurze drücker brauchts ne extra Var
bool button0State = false;
bool button1State = false;
bool button2State = false;
byte buttonCount = 0;         //Wie weit ist der Doppelklick schon fortgeschritten?

bool encTarget=0;             //encoder target wechselbar für Stunden/Minuten/Leuchtstärke(Nachtlicht)
unsigned long debTimer=0;     //Button debounce 

byte encVal = 0;
byte SleepTime=0;             //Wie lang soll die Lampe Leuchten bei Aktivierung des Sleeptimers?
bool sleepStell=false;        //wird gerade der Sleeptimer eingestellt?
int oldPosition=0;
int realBrigh=1;              //Durchschnittsbildung Helligkeitsmessung für Displayhelligkeit


// Array geht von 1-255 Wird nach jedem Durchlauf mit neuen Zufallszahlen beschrieben und braucht Platz 
// zum Abspeichern der schon gespielten Tracks
byte questionNumberArray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 
121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 
142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 
163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 
184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 
205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 
226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 
247, 248, 249, 250, 251, 252, 253, 254, 255};    

/* restliche Zahlen aus Array werden akut nicht benötigt da unter 100 Songs, Speicherplatz sparen@ atmega168. 
Zahlen hierhin verschieben:
---

---


-----*/
//---ende Array

byte shufflePos = 0;          //wieviele Tracks wurden schon gespielt?

//------- nötig Zum Auslesen/schreiben von DS3231------------------------------------
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val){
  return( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val){
  return( (val/16*10) + (val%16) );
}
//--------------------------------------------------------------

//---------------------------------------------------------------
void setup(){

  
 //Serial.begin(9600);                     //Debugging, if needed only.

//I2C für DS3231  
  Wire.begin();
//Eingänge
  pinMode (button0Pin, INPUT_PULLUP);
  pinMode (button1Pin, INPUT_PULLUP);
  pinMode (button2Pin, INPUT_PULLUP);

  pinMode (2, INPUT_PULLUP);            //encoder
  pinMode (3, INPUT_PULLUP);
  
//Ausgänge
  pinMode (AmpEnPin, OUTPUT);
  pinMode (PampEnPin, OUTPUT);
  //pinMode (DcdcEnPin, OUTPUT);
  digitalWrite(AmpEnPin,HIGH);          //Amp(dfPlayer) Aus, Logik invertiert 
  digitalWrite(PampEnPin,LOW);          //Amp(Pam8403) Aus
  delay(10);
     
//serielle Verbindung zum DFplayer aufbauen
mySerial.begin(9600);
  myMP3.begin(mySerial);
  delay(400);
  myMP3.volume(18);                     //Lautstärke (soll 18) Wird außerdem in void audioPlbk() gesetzt für Geräusche vs Musik
  delay(10);
  myMP3.EQSelect(3);                    //ohne Pam8403 -> EQ=3 ist cool,  mit Pam EQ=aus, sonst Clipping im DAC!
  delay(10);
 //byte volume = myMP3.currentVolume();
 
  folder01 = myMP3.numTracksInFolder(01);             //Einlesen von Anzahl Dateien in #folder
    delay(10);
  folder02 = myMP3.numTracksInFolder(02);
    delay(10);
  folder03 = myMP3.numTracksInFolder(03);
    delay(10);
  folder04 = myMP3.numTracksInFolder(04);
    delay(10);
 
  /*
  //Erkennung der Ordner, Anzahl Dateien
  Serial.print("folder01 count");                   //Kontrolle ob Kommunikation mit dfPlayer klappt, wenn nicht gibt er 255 für alle Verzeichnisinhalte aus
  Serial.println(folder01);                           //häufiger Fehler: 12v Powersupply fehlt u. Arduino hängt nur am USB
  Serial.print("folder02 count");
  Serial.println(folder02);
  Serial.print("folder03 count");
  Serial.println(folder03);
  Serial.print("folder04 count");
  Serial.println(folder04);
  
  Serial.println("starting...");  
  delay(10);                         */
  
  //7myMP3.sleep();                                  //Audio aus @Startup
  
 //TM1637 
display.setBrightness(2);

//16bit PWM
setupPWM16();                     //init
analogWrite16(ledPin, 0);         //PWM an LED=0
analogWrite16(disPin, 55534);     //PWM_DAC an Display=50000



  while(digitalRead(button2Pin)==HIGH){                     //@Startup wird randomSeed() verwendet, um den Zufall zu verbessern. 
    display.showNumberDec(millis(), true);
    }
  if (digitalRead(button2Pin)==LOW){                        //Quelle für randomSeed() ist Button2Pin / User Eingabe    
    randomSeed(millis());
    shuffle();                                              //gut mischeln
    shuffle();
    shuffle();
    shuffle();
    shuffle();
    }
}//^setup^-------------------------------------

//---------------------------------------------
void setupPWM16() {
  DDRB  |= _BV(PB1) | _BV(PB2);       /* set pins as outputs */
  TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                 /* mode 14: fast PWM, TOP=ICR1 */
  TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                  /* prescaler 1 */
  ICR1 = 65535;                         /* TOP counter value (freeing OCR1A*/
}
//---------------------------------------------
void analogWrite16(uint8_t pin, uint16_t val){
  switch (pin) {
    case  9: OCR1A = val; break;
    case 10: OCR1B = val; break;
  }
}
//---------------------------------------------
void shuffle(){

  byte n = folder03+1;

for (byte i = 0; i < n - 1; i++){
  
    byte j = random(0, n - i);

    int t = questionNumberArray[i];
    questionNumberArray[i] = questionNumberArray[j];
    questionNumberArray[j] = t;
    }
for (byte i = 0; i < n - 1; i++){

  Serial.print(questionNumberArray[i]);
  Serial.print(", ");
      
        }
   Serial.println(" ");     //*/
}

//-----audioPlbk()---------- Funktion zur Audiowiedergabe
void audioPlbk(byte what){

//wenn gerade gespielt wird, deswegen alles an ist und jetzt ausgeschaltet werden soll
//enthält Code zur Diagnose des dfPlayers. Popclick et Al.

//Wenn gespielt wird und ausgeschaltet werden soll 
  if (playing && !what){
           
      //myMP3.volume(0);                                           //PopKlickVerhinderungsversuch
      //delay(10);
      //Serial.println("Amp ausschalten");
      digitalWrite(AmpEnPin, HIGH);                                //AMP ausschalten(HIGH=aus!)
      digitalWrite(PampEnPin,LOW);                                 //Pam8403 ausschalten
      delay(10);
      }

//Wenn nicht gespielt wird und daher alles aus ist, anschalten     
   if(!playing && what){
      
      myMP3.volume(0);                                            //Popklickverhinderungsversuch
      delay(20); 
      digitalWrite(AmpEnPin, LOW);                                //AMP anschalten(HIGH=aus!)
      digitalWrite(PampEnPin,HIGH);                               //Pam8403 anschalten, High =an
      myMP3.volume(20);                                           //Lautstärke (war=18) einstellen wg powercycle 
      }

//Was soll gespielt werden?     
  if (playing!=what){                                             //immer nur ein Mal das Kommando senden
      playing=what;
      if (what==1){                                             
          myMP3.playFolder(what, random(1,folder01));         
          }
           
      if (what==2){
        myMP3.playFolder(what, random(1,folder02));
        }
      
      if (what==3){
        myMP3.volume(16);                                        //(war=12)etwas leiser als Alarm, ist Musik und daher höherer RMS als Ambient. Außerdem Chill mal ;)
        delay(10);
        myMP3.playFolder(what, questionNumberArray[shufflePos]);
        shufflePos++;
          if(shufflePos>=folder03){                               //Wenn alle Tracks gespielt wurden
            shufflePos=0;                                        //von vorne
            shuffle();                                           //neu mischeln
            }
        }
      
      if (what==4){
        myMP3.playFolder(what, random(1,folder04));
        } 
      
    //Serial.print("Spiele folder ");
    //Serial.println(what);  
       }
  }
//-----^audioPlbk()^-------------

//---dispTm1637 Anzeigefunktion-Led Display-----
/*-Variante ohne beleuchteten Button 1, Alarmstatus wird über Doppelpunkt angezeigt
 * 
//
//OLD -dispTm1637  Anzeigefunktion-Led Display----- OLD
void dispTm1637(byte Verbleib){
  
// erzeuge Zeitformat für die Anzeige:
int displaytime = 0;

if (Verbleib<1){
      if(ZeigeAl==false && RestSchlaf<1){
        displaytime=(hours * 100) + mins;       //aktuelle Uhrzeit anzeigen
         }      
      if(ZeigeAl==true ){         
      displaytime=(Ahours * 100) + Amins;       // Alarmzeit anzeigen/einstellen      
        }
  }
      
if (RestSchlaf>0){                              //verbleibende Zeit im aktuellen Alarmzustand
  displaytime=Verbleib; 
}



//Alarm An/Aus wird über den Doppelpunkt angezeigt //besser über separate Led, -> toDo, Hardwareupgrade nötig

if (AlarmV){
  display.showNumberDecEx(displaytime, 0b11100000, !Verbleib);    //Wenn Alarm an, Doppelpunkt an. (...~decEx) 
  }                                                               //shownumberDecEx(AnzuzeigendeZahl, DoppelpunktAn, padding/w Zeroes)
                                                                  //zero-Padding soll während Anzeige von Restschlaf aus sein wg Verwechslungsgefahr mit aktueller Uhrzeit
if (!AlarmV){                                                     //deswegen !Verbleib
  display.showNumberDec(displaytime, !Verbleib);                  //wenn Alarm aus, Doppelpunkt aus.
  }  
    
}
//OLD----------- /tm1637OLD--------------------------OLD

 * 
 */


void dispTm1637(byte Verbleib){
  
// erzeuge Zeitformat für die Anzeige:
int displaytime = 0;

if (Verbleib<1){
      if(ZeigeAl==false && RestSchlaf<1){
        displaytime=(hours * 100) + mins;       //aktuelle Uhrzeit anzeigen
         }      
      if(ZeigeAl==true ){         
      displaytime=(Ahours * 100) + Amins;       // Alarmzeit anzeigen/einstellen      
        }
  }
      
if (RestSchlaf){                              //verbleibende Zeit im aktuellen Alarmzustand, Doppelpunkt aus
  displaytime=Verbleib; 
    if (AlarmV){
        display.showNumberDecEx(displaytime, 0b10100000, !Verbleib);    //(alt:0b11100000) Wenn Alarm an, Tasterled. (...~decEx) 
        }                                                               //shownumberDecEx(AnzuzeigendeZahl, DoppelpunktAn, padding/w Zeroes)
                                                                        //zero-Padding soll während Anzeige von Restschlaf aus sein wg Verwechslungsgefahr mit aktueller Uhrzeit
    if (!AlarmV){                                                       //deswegen !Verbleib
        display.showNumberDecEx(displaytime, 0b00000000, !Verbleib);    //(alt:0b01000000)wenn Alarm aus, Tasterled aus, Doppelpunkt an.
        }    
   }



//Alarm An/Aus wird über den Doppelpunkt angezeigt //besser über separate Led, -> toDo, Hardwareupgrade nötig
if (!RestSchlaf){
    if (AlarmV){
        display.showNumberDecEx(displaytime, 0b11100000, !Verbleib);    //Wenn Alarm an, Doppelpunkt an. (...~decEx) 
      }                                                               //shownumberDecEx(AnzuzeigendeZahl, DoppelpunktAn, padding/w Zeroes)
                                                                  //zero-Padding soll während Anzeige von Restschlaf aus sein wg Verwechslungsgefahr mit aktueller Uhrzeit
    if (!AlarmV){                                                     //deswegen !Verbleib
        display.showNumberDecEx(displaytime, 0b01000000, !Verbleib);    //wenn Alarm aus, Tasterled aus, Doppelpunkt an.
      }  
  }   
}
//------------- /tm1637--------------------------

//-Helligkeitsanpassung Display-----------------
void setBrigh(){
                                                     //wenn kein Alarm, Helligkeit anpassen 
  const int heller=1200;                             //Offset für Korrektur der Helligkeit, falls Anzeige zu dunkel.
  int readBrigh = analogRead(HellPin);               //Mittelwertbildung für Sprungfreiheit, wichtig wen Licht an ist (PWM-> springende Werte an/aus)
      readBrigh=constrain(readBrigh, 0, 900);
      smooBrigh=(smooBrigh*5+readBrigh)/6;
              
  int writeBrigh = map(smooBrigh, 0, 900, 1,4);              //Dimminfunktion _im_ Display, ist nötig, da sonst der Strom bei wenig licht zu groß ist und Display flimmert.
  unsigned int disDAC= map(smooBrigh, 0, 900, 34000, 61535);  //Wenn unterer Wert zu niedrig  geht nix oder flimmerts, bei Vollgas ist die Stromaufnahme zu hoch für den PWM-Pin(?)

  

//Displayhelligkeit über TM1637
  display.setBrightness(writeBrigh);        //, 1-4 
   
//Displayhelligkeit über Versorgung
   if(smooBrigh>8){                                  //unterhalb 7 ist das Licht aus-> dunkles Display, oberhalb Helligkeitsboost
    analogWrite16(disPin, constrain((disDAC+heller),34000, 61535));           //65535 PWM_DAC an Display oberhalb 61535 ist der Stromverbrauch zu hoch für den Arduino Pin
   }
    if(smooBrigh<9){
  analogWrite16(disPin, disDAC);           // Boost ist ausgeschaltet,  PWM_DAC an Display
    }

/* Serielle Ausgabe für debugging des Helligkeitssensors
  Serial.print("Einlesewert:  ");                     //Kontrolle Displayfading
  Serial.print(readBrigh);
  Serial.print("  Wert Display: ");
  Serial.print(writeBrigh);
  Serial.print("  Wert PWM Ausgabe:     ");
  Serial.println(disDAC);                             // */
  
}
//---^setbrigh^---------------------------------

//Encoder mit ohne Beschleunigung (war race condition, wieder rausgeworfen)
void encoder() {
  
  int newPosition = myEnc.read()/4 ;      //Liest pro Rastung 4 neue Werte ein, daher /4
  if (newPosition != oldPosition) {
      int AccVal=(newPosition-oldPosition);
      oldPosition=newPosition;
      
//Bei encTarget=1 werden Minuten bei 0 Stunden verstellt      
      if (ZeigeAl && !AlarmV && !sleepStell){             //bei aktiviertem Alarm und bei Sleeptimereinstellung soll nix verstellt werden                                  
          if (encTarget){
            Amins = Amins+AccVal;            //Bei enctarget werden Minuten verändert
              }
          if (!encTarget){
            Ahours = Ahours+AccVal;            //Bei !enctarget werden Stunden verändert
              }

    //Minuten, Überlauf
      if (Amins>59&& Amins<201){                                //Überläufe in Plus-Richtung, Amins ist byte(unsigned) und läuft nach unten zu 255 über
          Amins=0;
          Ahours++;
          }    
      if (Amins>200){                                            //Überläufe in Minus-Richtung, Amins ist byte(unsigned) und läuft nach unten zu 255 über
          Amins=59;
          Ahours--;
          }
       
   //Stunden, Überlauf  
      if(Ahours>23 &&Ahours<201){                                //Überläufe in Plus-Richtung, s.o.
          Ahours=0;
          }      
      if (Ahours>200){                                           //Überläufe in Minus-Richtung, s.o.
          Ahours=23;      
          }               
      }
                    
           
    
    
  
//Bei lichtAn=1 wird Helligkeit eingestellt    
//bei Licht an, gehen die Werte bis 48, kein Überlauf zu 0/48 wg Blendwirkung von 0->48, dunkel nach hell, stattdessen Wert halten
      
      if (lichtAn && !ZeigeAl && !sleepStell){
        if(encVal>0 || encVal<48){
          encVal = encVal+ AccVal;                      //keine Beschleunigung in der Beleuchtungsfunktion
          if (encVal>100){                              //Überlauf von byte 0 -> 255 abfangen. Möglich wg Beschleunigung
              encVal=0;
              }
          if (encVal>48){                               //hellste Stufe der Beleuchtung, bei constrain meckert der Compiler, warum auch immer(?)
              encVal=48;
              }
          Leucht(encVal);  
          }
      }
// Bei sleepStell=1 Wird SleepTimer eingestellt
     if (sleepStell){
        
          SleepTime = SleepTime + AccVal;            //   MaximalZeit ist 255Min wg Überlauf byte
          RestSchlaf=SleepTime;                      //   Ausgabe über Display via RestSchlaf
      }

        
  }  
}   
//----^encoder^--------------------------------------
//---Knöpfe einlesen, Dinge tun -----------------------------------------
void buttonLogic(){

  button0State = digitalRead(button0Pin);             //Encoder Pushbutton
  button1State = digitalRead(button1Pin);             //roter Knopf vorne
  button2State = digitalRead(button2Pin);             //schwarzer Knopf oben(arcade)
  
  if(button2State==LOW || lichtAn){                    //Encoder muss eingelesen werden
     encoder();
     }

//Umschaltung encTarget Minuten/Stunden
if(!button2State && !button0State && (millis()-debTimer)>300 ){                //nur ausführen, wenn Alarmzeit angezeigt wird, sonst kann der Sleeptimer nicht aktiviert werden (kollision mit debtimer)
    encTarget=!encTarget;
    debTimer=millis();
    }

//--Erkennung von kurzen/langen Drückern auf button1-- Roter Knopf
if(button1State==LOW && button1Pressed==false ){        //press-Event 
     LpTimer=millis();  
     button1Pressed=true;
     }

if(button1State==HIGH && button1Pressed==true){        //release-Event (2 Möglichkeiten)
     
//kurzer Knopfdruck = umschalten Alarm an/aus
     if(millis()-LpTimer>20 && millis()-LpTimer<4000){ 
        
        AlarmV=!AlarmV;                                //Wenn Alarm an, Alarm= aus
        
        if(AlarmV){                                   //Wenn Alarm an, rAmins&rAhours schreiben für früheren Sonnenaufgangsbeginn                                

      //Umrechnung der 30 Minuten Vorlauf auf Stunden             
          if (Amins>29 ){                 //Minuten 30-59, Stunden 0-23  
              rAmins = Amins-30;                      //  
              rAhours = Ahours;
              }      
          if (Amins<30 && Ahours>0){                  //Minuten 0-29, Stunden 1-23  
              rAmins = Amins+30;
              rAhours = Ahours-1;
              }
          if (Amins<30 && Ahours==0){                 //Minuten 0-29, Stunde 0
              rAmins = Amins+30;
              rAhours = 23;
              }                          
           }
           button1Pressed=false;         
        }

//langer Knopfdruck = Uhrzeit setzen
     if(millis()-LpTimer>6000 &&AlarmV==false){      
        
        setDS3231time();                            // schreibt aktuelle Amins&AHours in DS3231
        button1Pressed=false; 
        }     
    }


//(neu) Button2 (Arcade) Einfach- und Doppelklick

//-press-Event ------
    if(button2State==LOW && !buttonCount &&  millis()-debTimer > 600){     
        buttonCount=1;
        debTimer=millis();
        Serial.print("buttonCount");
        Serial.println(buttonCount);
        }
//release-Event
    if(button2State&& buttonCount==1 &&millis()-debTimer>10){      
        buttonCount++;
        Serial.print("buttonCount");
        Serial.println(buttonCount);        
        }
//-Doubleclick -Event
    if(buttonCount==2 &&!button2State && millis()-debTimer>10 &&millis()-debTimer<270){       //Test-Op:             
        
        lichtAn=!lichtAn;
    
        if(lichtAn){                    //Licht wird angeschaltet, Startwert Licht ist 24 von 48 Stufen
          encVal=24;
          Leucht(encVal);             //muss hier einmal ausgeführt werden, weil encoder() nur bei veränderungen aufruft
          }
        if(!lichtAn){                   //Licht wird ausgeschaltet, Encoder braucht die Werte aus Zeiteinstellung (es wird zu den Minuten gesprungen)          
          leuchtVal1=0;               //Leucht() kann nur licht anschalten, aber nicht aus, deswegen hier
          SleepTime=0;               //Sleeptimer ausschalten
          RestSchlaf=0;               //zur Sicherheit mal auf 0 setzen (obsolet?)
          }
    buttonCount=0;      
    debTimer=millis();
    Serial.print("buttonCount");
    Serial.println(buttonCount);
    //Serial.print("millis()");Serial.println(millis());
    }
    
//-erase event -----
    if(millis()-debTimer>270&&buttonCount){
        buttonCount=0;
        Serial.print("buttonCount");
        Serial.println(buttonCount);
        }



//Sleeptimer Einstellung aktivieren
     if(lichtAn && !SleepTime && !sleepStell && !button0State && millis()-debTimer>400){
        sleepStell=1;                                                                       //Verstellung des Timers über Encoder wird aktiviert
        SleepTime=10;                                                                       //Default =10 Minuten
        RestSchlaf=SleepTime;                                                               //Anzeige im Display über Variable RestSchlaf
        debTimer=millis();
       }
//Sleeptimer setzen und aktivieren, Einstellung abschalten
     if(lichtAn && SleepTime && sleepStell && !button0State && millis()-debTimer>400){
        sleepStell=0;                                                                       //Einstellung fertig, deaktivieren
        fadePointer=millis();                                                               //fadePointer ist eine recycelte Variable, die auch in der Weckroutine verwendet wird. Dient hier für Zeitmessung seit sleeptimeraktivierung
        debTimer=millis();
       }

  

//Alarmzeit soll angezeigt werden
 if(button2State==LOW && !AlarmState){           
        ZeigeAl=true;                                //notwendig auch zum Einstellen des Alarms u. der Zeit    
    }
    
 if (button2State==HIGH){
    ZeigeAl=false;  
    }

    
//Alarmlogik Button 2 (Arcade Button)   
 if(buttonCount==1 && AlarmState == 1 ){      //Weckphase 1 läuft, Schläfer ist wach-> Sprung zu 3, extra lange Debounce-Zeiten weil schläfriger User
    fadePointer=millis();                                                     //Zähler reset für einfacheres Debugging
    fadeZaehl=0;
    AlarmState=3;
    }
 if(buttonCount==1 && AlarmState == 2 ){     //WeckPhase 2(lauter als1) läuft, Schläfer ist wach-> Sprung zu 3
    fadePointer = millis();                                               //Zähler reset für einfacheres Debugging
    fadeZaehl = 0;
    AlarmState = 3;
    }
 if(buttonCount==1 && AlarmState == 3 && millis()-fadePointer >900 ){     //Schläfer steht auf und verlässt das Bett
  debTimer = millis();
  fadePointer = millis();
  fadeZaehl = 0;
  AlarmState = 4;                                                       
    }
 if(buttonCount==1 && AlarmState == 4 && millis()-fadePointer >700){     //Schläfer will das Licht schnell aus haben
  fadeULong = 100;                                                      //Alarm() A-state=4 macht den Rest
   }
}//close ButtonLogic



//Lichtfunktion, bekommt bei lichtAn==1 Werte von encoder()und macht daraus logarithmische Helligkeitsstufen
void Leucht(byte intens){
  unsigned int leuchtVal= 10;
   
  for (byte i = 0; i <=intens; i++) {       //logarithmische Berechnung der Helligkeit
    leuchtVal=leuchtVal*1.2;
     }
  if(leuchtVal<13){                         //minimal-Licht zur Verwendung als Nachtlicht
      leuchtVal=9;
     }

  leuchtVal1= leuchtVal;                    //Ausgabe mit smoother(), der tut jetzt 
  //alt: analogWrite16(ledPin, leuchtVal);
  
                       
//Sleeptimer: automatische Lichtabschaltung nach via Encoder und Buttonlogic eingestellte Minuten   

if(!sleepStell && SleepTime && (millis()-fadePointer)>SleepTime*60000L){
  
    encTarget=0;    
    leuchtVal1=0;                           //Ausgabe mit smoother()
    SleepTime=0;
    lichtAn=0;
    }
}
//-------^void Leucht()^----------------------------------------------------------------------------------------

//---- smoothing für stufenlose Helligkeitseinstellung
void smoother(){

      if(leuchtVal1 != LedPWMVal){
      
        LedPWMVal=(20*LedPWMVal+leuchtVal1)/21;
        if(LedPWMVal<leuchtVal1 && leuchtVal1-LedPWMVal<30){
           LedPWMVal++;                                         //Bei steigenden Werten wird sonst kein Endwert erreicht
            }
        
         /* if(LedPWMVal<5){                  //not needed
              LedPWMVal=0;*/
              
                  
      analogWrite16(ledPin, LedPWMVal);

      /*
      Serial.print("leuchtVal1= ");
      Serial.print(leuchtVal1);  
      
      Serial.print("LedPWMVal= ");
      Serial.println(LedPWMVal);  // */
      }
    
   
 }//---Ende smoother 

//-----------------------------------------------------------------
void setDS3231time(){
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(00)); // set seconds
  Wire.write(decToBcd(Amins)); // set minutes
  Wire.write(decToBcd(Ahours)); // set hours
  //Wire.write(decToBcd(1)); // set day of week (1=Sunday, 7=Saturday)
  //Wire.write(decToBcd(1)); // set date (1 to 31)
  //Wire.write(decToBcd(1)); // set month
  //Wire.write(decToBcd(0)); // set year (0 to 99)
  Wire.endTransmission();
}
//----------------------------------------------------------------------

void readDS3231time(){
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);                                // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  byte second = bcdToDec(Wire.read() & 0x7f);   //wird gelesen, aber nicht gebraucht. 
  mins = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
  //*dayOfWeek = bcdToDec(Wire.read());
  //*dayOfMonth = bcdToDec(Wire.read());
  //*month = bcdToDec(Wire.read());
  //*year = bcdToDec(Wire.read());
}

//--------------------------------------------------
void alarm(){
  //Sonnenaufgang: nach #Zeitintervall wird der bisherige Helligkeitswert mit 1,01 multipliziert. 
  //~22Minuten Fade
  //MP3Start nach 12Minuten, random Track in Folder 01, Soundscapes sind mit Fade-In überlagert, Lautstärkekontrolle am dfPlayer Mini hat nur 30 Stufen (reicht nicht)
    
const int upSpeed=1000;                   //(1000neu=>20+x(?) Minuten, 100=>127sek.) sets Interval for multiplication of fadeULong(fade in)
                                          //kleinere Werte=schnellerer Fade 
const int dnSpeed=6;                      //(6) sets Interval for division of fadeULong (fade out)
const int lowMinim=20;                   // Offset für Einstellung der niedrigsten Helligkeitsstufe, damit der langsame Teil des Fades auch im nützlichen Helligkeitsbereich ist
                                          //sollte nicht über 800 sein, sonst wird die auskommentierte constrain funktion in Sonnenaufgang notwendig  
unsigned long fadeTime=(millis()-fadePointer);  //wie lange läuft der Alarm schon? FadePointer bekommt von Buttonlogic() Reset bei Sprüngen in A-states 3+4
unsigned int fadeVal=1;                   //local Var für Ausgabe an 16Bit PWM



//Alarm Stufe 1: Sonnenaufgang, Gezwitscher
  if (AlarmState==1 && fadeTime < 1200000){
    
//pre-sunrise linearer fade-in für niedrige Helligkeit
    if(fadeTime<100){                           //nur 1x am Anfang ausführen
         
              leuchtVal1=lowMinim;
              //Serial.print("1schnell= ");
              //Serial.println(i); 
          }
    
//Start Audio wenn FadeTime>x
    if(fadeTime>900000 && playing==0){                      //12min nach Beginn(900K Millisekunden)                                              
      audioPlbk(1);                                       //spielt Ordner 1 (Vogelgezwitscher, Ambient Sounds)  
       }    


//Sonnenaufgang Berechnung der Helligkeitswerte abhänging von bereits abgelaufener Alarmzeit
    if ((fadeZaehl+upSpeed)<fadeTime  &&fadeULong<4901480){   //Fade-Funktion wird ausgeführt, solange keine 5M erreicht werden können
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong*1.01);                               //
      fadeVal=fadeULong/76.3+lowMinim;                          //5000000/76,3= 65535, der 100%wert für PWM16, map() macht mist bei Werten größer als int
      leuchtVal1 = fadeVal;
      //Serial.print("1langsam= ");
      //Serial.println(fadeVal); 
        }
        
  }
      
// Maximum, ist erreicht(1800000ms/30 Minuten preset)       //Nach T>30 Minuten weiter bei AlarmState2,
    if(AlarmState==1 &&fadeTime>1800000){
        AlarmState=2;
        }

//Alarm Stufe 2: Vollgas Licht, laute Weck-Musik 
    if(AlarmState==2){                                        //schläft noch&& bisherige Maßnahmen zeigten keine Wirkung
      fadeULong=5000000;                                     //maximum Warp
      fadeVal=65535;
      leuchtVal1 = fadeVal;
      if(playing!=2){
          audioPlbk(2);                                        //spielt Ordner 2 (laute Weckroutine)
          }
        
        //renitenter Schläfer braucht nach 35Min noch härtere Gangart
     if (fadeTime>2100000){
        playing=1;          //sonst nimmt audioPlbk() keine Befehle an
        audioPlbk(2);       //<-Track erstellen und neues Verzeichnis reinkopieren, nummer hierhin                  //35minuten Neuer, aggressiver Wecktrack, volle Lautstärkespielt laute Weckroutine  
        }

//40 Minuten vorbei. Genug geweckt, ist keiner Da-> ausschalten!           
     if (fadeTime>2400000){                                     
      AlarmState=4;                                     //AlarmState=4 macht das Licht aus
      AlarmV=false;
      }
    
  }//close AState2


//Alarm Stufe3:  ist wach, braucht aber noch Licht, Fadeval auf angenehmen Wert 
    if(AlarmState==3){                                    
        if(playing!=3||playing==1){
          audioPlbk(3);                                    //Spielt Ordner 3 (angenehme leise Musik)
          }


       if(fadeULong>2000000){                           //angenehm hell, mit zwischenFadeout
          if ((fadeZaehl+dnSpeed)<fadeTime){
              fadeZaehl=fadeTime;
              fadeULong=(fadeULong/1.01);                
              }
              if(fadeULong<2010000){   
              fadeULong=2000000;
              }
            }
                      
        if(fadeULong<2000000){                           //angenehm hell, mit zwischenFadein
        
            if ((fadeZaehl+dnSpeed)<fadeTime){
              fadeZaehl=fadeTime;
              fadeULong=(fadeULong*1.01);  
              }
              if(fadeULong>1990000){   
              fadeULong=2000000;
              }
            }
            
          fadeVal=(fadeULong/76.3)+lowMinim;            //Ausgabe über Smoother
          leuchtVal1 = fadeVal;                         //Wert wird gehalten */
               
      
         /*umbauweg: fadeULong=2000000;                           //angenehm hell, mit zwischenFadeout
          fadeVal=(fadeULong/76.3)+lowMinim;
          leuchtVal1 = fadeVal;                       //Wert wird gehalten */
          
                          
       //Serial.print("3hold= ");
       //Serial.println(fadeVal);

       
      if (fadeTime>600000){                //ist Wach, hat Lichtausmachen vergessen(10 Minuten nach ButtonPress)
          AlarmState=4;  
          }
   }//close Astate3

 //Alarm Stufe 4: Es wird aufgestanden, Licht kann ausfaden, kleiner Soundschnipsel als Bestätigung 
  if(AlarmState==4&&fadeULong>100){                         //steht jetzt auf Licht soll ausfaden
    
    if(playing!=4){                       //Spielt Ordner 4 (kurzer Bestätigungssound, +1Up Mario)
       audioPlbk(4);                                     
       }
       
    if ((fadeZaehl+dnSpeed)<fadeTime){
      fadeZaehl=fadeTime;
      fadeULong=(fadeULong/1.01);  
      fadeVal=(fadeULong/76.3);
      leuchtVal1 = fadeVal;
      //Serial.print("4 out= ");
      //Serial.println(fadeVal);     
      }    
  }

  
  if(AlarmState==4&&fadeULong<101){                          //Fade ist durch, alles zurück auf Ausgangswerte/abschalten
      fadeULong=100;
      fadeZaehl=0;
      AlarmState=0; 
      audioPlbk(0);
      AlarmV=false;
      leuchtVal1 = 0;
      } 
  
    
}
//----------^void alarm()^-------------------------------

void loop(){
 
  if (schedulT<millis()){                                      //Aufrufe begrenzen
  
      readDS3231time();                                        //Zeit einlesen 
      setBrigh();                                    //Helligkeit im Display an Umgebung anpassen, bei AlarmState>0 max Brightness 

 //Restschlafanzeige und Sleeptimeranzeige
      if (AlarmState || (SleepTime&&!sleepStell)){
        zeigeRS=!zeigeRS;                           // Abwechselnd Uhrzeit + Restschlaf anzeigen
        if (zeigeRS&&AlarmState==1){
              RestSchlaf=((1800000-(millis()-fadePointer))/60000);  //Weckroutine dauert 30 Minuten 1 800 000ms + 59000 für Trunkierung Ganzzahlarithmetik 
            }
        if (zeigeRS&&AlarmState==2){                                //A-State=2 Heißt Zeit ist abgelaufen
              RestSchlaf=0;
            }  
        if (zeigeRS&&AlarmState==3){
              RestSchlaf=((659000-(millis()-fadePointer))/60000);   //hat 10 Minuten Zeit zum Anziehen
            }
    //Sleeptimer anzeigen        
        if (zeigeRS && !AlarmState){
          RestSchlaf= ((fadePointer + SleepTime*60000L)-millis()+59000)/60000 ;          // Wie lange noch bis zur automatischen Lichtabschaltung?
          
          Leucht(encVal);                                            // Wird sonst nur bei Encodereingaben aufgerufen, dann bleibt das Licht an, auch wenn Zeit abgelaufen ist. 
          }
                   
        if (!zeigeRS){                                               // Bei Restschlaf= 0 wird nur die aktuelle Uhrzeit angezeigt
              RestSchlaf=0;
            }         
        }
      
     if (!AlarmState && !encTarget &&!SleepTime){           //fängt Anzeige von Restschlaf ab, wenn nicht benötigt (obsolet?)
          zeigeRS=false;
          RestSchlaf=0;
          }

 //Zeit für Alarm?
        if(AlarmV==true &&AlarmState==0 && hours==rAhours && mins==rAmins){       //Bedingung für Alarmauslösung 
              fadePointer=millis();                                               //Pointer setzen für Zeitablauf
              //lichtAn=false;
              lichtAn = 0;            //Licht ausschalten, falls an
              SleepTime=0;            //Sleeptimer reset
              AlarmState=1;        
          }     
     

     /*Serial.print("AlarmState: " );
     Serial.println(AlarmState);
     Serial.print("fadeULong= ");
     Serial.println(fadeULong);     */      

    // 1x pro Sekunde  

    
     schedulT= millis()+1000;                                   
  }//scheduler zumachen
  
   
 //Solange der Alarm läuft, wird die zugehörige Funktion auch aufgerufen  
  if(AlarmState){
      alarm();
    }
    
//Knöpfe einlesen
 buttonLogic();   
                                             
// display auf dem tm1637   
 dispTm1637(RestSchlaf);
// smoother aufrufen
smoother();                                                 
 
                                              
}//loop


Außerdem hab ich noch ne kleine Bedienungsanleitung geschrieben. Beidseitig ausdrucken auf A4, in der Mitte falten, fertig.

Grüße
Mo
Dateianhänge
Lichtwecker Bda_09.pdf
(196.18 KiB) 33-mal heruntergeladen
Zuletzt geändert von Später Gast am Do 4. Mär 2021, 19:25, insgesamt 1-mal geändert.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

N'Abend zusammen,

es gibt noch Fortschritte hier:

Ich habe mittlerweile LED-beleuchtete Taster gefunden, bestellt und erhalten, die nicht kackhässlich sind und am Wecker verbaut werden dürfen: Ebay

Um den leuchten zu lassen hab ich beim Treiberchip vom LED-Display mal nach unbenutzten Ausgängen gesucht und bin fündig geworden, ich kann den jetzt über die Library ansteuern, als wäre er ein Dezimalpunkt. Einziges Problem war die Angleichung der Tasterled an die Displayhelligkeit, nur mit Vorwiderstand wars entweder bei hellem Display zu dunkel oder bei dunklem Display zu hell, aber ne Diode in Reihe hat geholfen, die Spannung so zu senken, dass das in etwa passt jetzt.
Hab mal versucht, das abzulichten, aber der wirkliche Helligkeitseindruck ist mit Fotos nicht darstellbar, zumindest nicht ohne größere Klimmzüge...

Bei Dunkelheit, an
7Weckkl_01.jpg
bei Helligkeit, an
7Weckkl_06.jpg
und bei Helligkeit, aus.
7Weckkl_07.jpg

An der Software gings auch voran, eimal Bugfixes: Zb gab es n paar Timerabläufe, die nicht sauber mit millis()-timer>xy gelöst waren und bei meiner Schwester dann nach 50 Tagen Betrieb dazu geführt haben, dass plötzlich ein Taster nicht mehr ging. :oops:

Aber es gibt auch einige neue Features, die "nach dem Aufwachen-Musikberieselung" wird jetzt geshuffelt und nicht randomisiert. Der dafür notwendige Array verbraucht allerdings soviel Platz, dass der 168er Nano jetzt zu klein ist.
Weiters kann man die Beleuchtung jetzt von einem Sleep-Timer abschalten lassen.
Dann hab ich für die Helligkeitsverstellung noch ne Zwischenstufen-Berechnung gebastelt, so wirkt die Verstellung jetzt stufenlos, was sehr angenehm ist. Bei den ganz niedrigen Helligkeitsstufen sieht man die einzelnen PWM-Schritte noch, aber das lässt sich bei logarithmischer Abstufung wohl nicht vermeiden.
Ich überlege noch, für den Timer eine Sonnenuntergangs-Simulation einzubauen, dass man auch sagen kann ich les jetzt noch ne halbe Stunde und wenns zu dunkel zum Lesen ist, leg ich das Buch halt weg und mach die Äuglein zu. :)

Aktuellen Code mach ich gleich noch in den Post eins drüber rein, der is eh verbuggt, will ja keiner haben sowas. :-P

Was die Benutzung/Funktion angeht bin ich sehr angetan, das ist wirklich eine sehr angenehme Art, das Problem aufstehen müssen, aber nicht wollen zu entschärfen. Selbst wenn ich viel zu spät ins Bett gegangen bin, macht mir das keine schlimme Bäh geh weg mit dem Gepiepse! - schlechte Laune mehr morgens. Meine Schweser ist auch nur begeistert und war erst ganz geknickt, dass ich das Gerät zum Kundendienst haben wollte, weil ich erst an ein abbes Kabel oder nen futschen Mosfet dachte, bis sie das Gerät zwischendurch mal vom Strom genommen hatte. Frontpanel abschrauben und gepatchte Firmware flashen kriegt sie hin, und bis es wieder eng wird hat sie noch 40 Tage Zeit...

Grüße
Moritz
Benutzeravatar
Fritzler
Beiträge: 12602
Registriert: So 11. Aug 2013, 19:42
Wohnort: D:/Berlin/Adlershof/Technologiepark
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Fritzler »

Also ich persönlich find ja die hier *rschgeil:
https://de.aliexpress.com/item/32899654 ... 458aJTw05j
Viel günstiger sind die auch noch 8-)
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

Stimmt, die sehen gut aus. Für den Wecker wären mir die aber ne Spur zu clean. Ich wollte auch, dass der Taster selbst ein Stückchen aus dem Gehäuse rausragt und nicht so ganz glatt anliegt. Sonst müsste ich auch den Encoderknopf nochmal ändern, das soll ja irgendwie zusammenpassen.

Das von mir verlinkte Angebot sind übrigens 9x2=18 Stück, das ist schon noch günstiger. Wie die auf die Idee mit dieser merkwürdigen Zählweise kommen ist mir aber ein Rätsel. Ist ja fast wie bei den Franzosen. ;)
sysconsol
Beiträge: 4059
Registriert: Fr 8. Jul 2016, 17:22

Re: Lichtwecker n+1, Arduino

Beitrag von sysconsol »

Etwas OT, aber für das Protokoll:
Anse hat geschrieben: Mo 23. Mär 2020, 00:20 Beim Energieverbrauch sind LCDs auch deutlich besser.
Ich hatte diese Problemstellung auch, vor allem im Zusammenhang mit dem Kontrast und Akkubetrieb.
Ich habe 4 Ziffern zu je 7 Segementen so angesteuert, dass immer nur eines der 28 Segmente leuchtet.
Die Stromaufnahme ist damit mehr oder weniger konstant und geht nicht über die Stromaufnahme einer LED hinaus.

Allerdings war das kein Arduino mit Bibliotheken, sondern C auf einem PIC.
Ändert jedoch nichts am Prinzip, falls das mal wer benötigt. So als Anregung.
Benutzeravatar
Später Gast
Beiträge: 1704
Registriert: Di 5. Apr 2016, 22:03
Wohnort: Karlsruhe
Kontaktdaten:

Re: Lichtwecker n+1, Arduino

Beitrag von Später Gast »

So OT ist das gar nicht. Die Library steuert nämlich nur 4 Dezimalen an, auch wenn der Chip 6 könnte. Vllt könnte sie das auch und ich war zu faul es rauszufinden. :roll:
Dadurch liegt der Dezimalpunkt/die Tasterled auf einer Ziffer, die in Benutzung ist, und für die Anzeige verbleibender Minuten des Sleep-Timers und des Sonnenaufgang- Alarmablaufs schalte ich die vorderen beiden Ziffern ab. Die Stromregelung des Treiberchips misst aber nur segmentweise, wenn der Rest von der Ziffer aus ist, leuchtet das verbliebene Segment also sichtbar heller. Die Folge ist, dass bei aktivierter Tasterled und Anzeige von Restminuten die Tasterled zwischen leuchtet und leuchtet heller "blinkt".

Wenn jetzt immer nur ein Segment gleichzeitig an wäre, träte das Problem nicht mehr auf. Ich fürchte nur, da reicht mein Perfektionismus grade nicht aus, das anzugehen. Klingt, als bräuchte man da recht genaues scheduling, man will ja auch nicht nur das Display beschicken, sondern auch n paar andere Sachen rechnen. Was ich mal probiert hab, ist das ganze Display über aus- und anschalten zu dimmen. Quasi soft-PWM. Furchtbar. :lol:
Antworten