• 5% ist wohl etwas zu viel. Der Controller hätte 96,5h * 60 * 60.000 = 34.740*104 ms Takten müssen, hat aber nur 34.716 *104 ms geschafft. Also statt 5.790 Minuten nur 5.786 Minuten. Darum würde ich 34.716*104 ms / 5790 = 59.958,55ms rechnen und es mal mit dem Wert 59.959 versuchen.


    Ein um 5% reduzierter Wert von 60.000 wäre 57.000 was wahrscheinlich zu niedrig ist.


    Ups... habe mich verlesen... Du hast ja 0,05% geschrieben....

  • Hallo Kai,

    die Uhr läuft von Montag 12:00 Uhr bis heute 12:30 ca 4 Minuten nach.

    In de Zeile

    Code
    1. constexpr long MILLIS_PER_MIN {60000}; // milliseconds per a minute

    müsste ich den Wert 60000 MILLIS_PER_MIN um ca. 0,05% verringern. Oder bist Du anderer Meinung?


    Grüße, Franz

  • Ich freue mich, dass Euch die Uhr gefällt.


    @Pius, DCF77 Empfang habe ich in meiner Wohnung nur in der Küche und selbst in diesem Raum nur mit Einschränkung. Nach Batteriewechsel muss ich die Küchenuhr oft auf die Terrasse zur Synchronisierung bringen.


    @Kai, Ich werde die Uhr ein paar Tage laufen lassen und dann berichten.


    Grüße, Franz

  • #Teckelfreund

    Die Uhr ist super geworden, vorallem für einen, der sich mit 3D Druck und Co nicht auskennt.
    Mein Respekt und nun eine DCF77 Synchronisierung?
    Muss ja nicht sein, die staunenden Blicke auf die Uhr reichen aus.

    Pius

  • Die größere Box für die Uhr habe ich gezeichnet. Zusätzlich habe ich noch an der Innenwand eine Motorpositionierung angebracht.

    Bis jetzt habe ich den Motor mit einer Schaumstoffunterlage positioniert.

    Auf der linken Schmalseite will ich noch ein Loch für den Einbau einer Klinkensteckerbuchse und auf der rechten Seite ein Loch für den Taster hinzu fügen.

    Unten im Bild sieht man zum Vergleich das jetzige Gehäuse.


    uhrbox4.jpg

  • Nach Pius' Hinweis mal eine Version, die auch funktionieren müsste, aber 24 Byte RAM einspart (43 statt 67 Byte):


    Nochmal als Ganzes:

  • Teckelfreund: Schön dass es klappt. Die Genauigkeit dürfte über einen längeren Zeitraum nicht besonders gut sein, weil die Nano Clones nur mit einem Resonator betrieben werden und die noch einmal etwas ungenauer als Quarze sind. Über die Dauer einer Nacht, dürfte es aber halbwegs passen….


    @Pius: Danke für Deinen Kommentar und den Code Review :). Deinen Aussagen kann ich nur zustimmen. Das „const“ bei der Operatorüberladung habe ich schlicht vergessen bzw. übersehen. Ich werde das dem Code noch hinzufügen. Der „Arduino-Compiler“ ist der Gnu C++ Compiler und ist, sofern man die Arduino IDE verwendet, auf den C++ 11 Standard eingestellt. Man kann den Standard über eine Konfigurationsdatei aber auch auf die aktuelle C++ 20 Version ändern.


    Es wird zwar viel „Arduino“ genannt, aber was das Programmieren betrifft, so ist man relativ frei. Wenn man die Funktionen setup() und loop() nicht verwendet, kann man auch „klassisch“ mit void main() und einer eigenen Endlosschleife programmieren.


    Der große Vorteil der Umgebung ist, dass man sich nicht so sehr mit den programmiertechnischen Details der μController herumschlagen muss und somit der Programmcode wesentlich portabler ist, als wenn man „hardwarenäher“ programmiert. Das ist weniger performant, was jedoch bei vielen Anwendungen keine Rolle spielt. Wenn man will kann man hier (fast) genau so Programme schreiben wie mit der Microchip (AVR) Studio Umgebung. Allerdings ohne Debugging und das sonstige "Zubehör" was die Studio Software mitbringt.


    Die Phasentabelle habe ich so vom Originalcode übernommen ohne mir groß Gedanken darüber zu machen. Ich habe sie lediglich mehr zum C++ Style geändert und die Magic Numbers durch „sprechende“ typisierte Konstanten ersetzt und den Datentyp von int auf byte (unsigned char bzw. uint8_t) geändert. Das hat schon, ohne groß du Denken, 50% RAM gespart.


    Was das „if“ bzw. „switch“ Statement betrifft… Ich mag If Bedingungen nicht so gerne, weil ich switch Anweisungen besser zu lesen finde. Wobei es stimmt, dass es bei solch einem kurzen Entscheidungszweig mit „If“ etwas kürzer formuliert werden kann. Das ist eher ein Spleen von mir.

  • Super Kai


    beeindruckend, echt da wird C++ auch benutzt.

    Kleiner Hinweis, ich weiss nicht ob die Arduino C++ Compiler dies auch unterstützen (denke schon).

    Code
    1. class Timer {
    2. public:
    3. void start() { timeStamp = millis(); }
    4. bool operator()(const unsigned long duration) { return (millis() - timeStamp >= duration) ? true : false; }
    5. private:
    6. unsigned long timeStamp {0};
    7. };

    In obiger Klasse fragt man mit dem Operator ab, ob die Zeit erreicht ist. Soweit ich feststelle verändert der Operator den Inhalt der Klasse Timer nicht.

    In solchen Fällen, kann man den Compiler dies mitteilen mit "const" am Ende


    Code
    1. bool operator()(const unsigned long duration)const {
    2. return (millis() - timeStamp >= duration) ? true : false;
    3. }

    In allen Fällen, wo man mit der Klasse Timer dann weiterarbeiten möchte und diese selbst als const deklariert haben möchte, weil vielleicht nicht alle "Benutzer" der Klasse eine Veränderung im Inhalt der Klasse erlaubt sein soll. Im obigen Fall denke ich, ist es nicht notwendig.

    Aber ich habe mir angewöhnt alle Memberfunktionen einer Klasse, die den Inhalt der Klasse selbst nicht verändern mit dem const zu deklarieren.


    Code
    1. constexpr byte seq[NUM_SEQUENCES][NUM_PORTS] {
    2. {LOW, HIGH, HIGH, LOW },
    3. {LOW, LOW, HIGH, LOW },
    4. {LOW, LOW, HIGH, HIGH},
    5. {LOW, LOW, LOW, HIGH},
    6. {HIGH, LOW, LOW, HIGH},
    7. {HIGH, LOW, LOW, LOW },
    8. {HIGH, HIGH, LOW, LOW },
    9. {LOW, HIGH, LOW, LOW }
    10. };

    Bei dieser Tabelle könnte man nochviel Platz einsparen, da ich annehme dass der Compiler für LOW/HIGH jeweils ein Byte benutzt. Aber solange man nicht knapp mit Flash da steht, ist Deine Lösung schöner zu lesen.
    Ich stehe im Augenblick in einem meiner Projekte ganz hart an der Grenze des verfügbaren Flash Speichers und verbringe Tage damit um vielleicht 100 Byte einzusparen. So bin ich soweit, dass ich

    einen f(float n) Aufruf als f(float *pn) realisiere. Ein float belegt 4 Byte, ein Zeiger auf ein float belegt bei meiner 8-Bit CPU nur 2 Byte ...

    Es ist anstrengend ja, aber wenn Du dubuggen willst und der Compiler meldet dass ich 4 Bytes zu viel Code produziere, dann muss ich im Debug Mode Texte verkleinern und eben auch *pfloat anstelle einer Kopie auf dem Stack benutzen. Eine weitere Erkentnis war, dass ich öffters mal das Schlüsselwort "register" für lokale Daten in Funktionen benutze, was oft zu kleinerem Code führt.

    Code
    1. switch (btn.tick()) {
    2. case Btn::ButtonState::longPressed:
    3. rotate(STEPS_PER_MINUTE);
    4. timer.start();
    5. break;
    6. default: checkForNextMinute(timer); break;
    7. }

    würde man das switch mit einem if() else ersetzen:

    Code
    1. if (btn.tick() == Btn::ButtonState::longPressed) {
    2. rotate(STEPS_PER_MINUTE);
    3. timer.start();
    4. }
    5. else {
    6. checkForNextMinute(timer);
    7. }

    spart man etwas Platz ein. Was aber nur Sinn ergibt, wenn das switch case wenige Überprüfungen hat.

    Es macht Spass Code von Dir zu lesen!;)

    schönen Abend

    Pius

  • Teckelfreund : Ich habe da mal was gebastelt. Ohne Funktionsgarantie....


    Der Servo wird jede Minute angesteuert, um die Uhr eine Minute weiter zu drehen. Wenn Du den Taster dauerhaft gedrückt hältst, wird im 200ms Takt je eine Minute vorgestellt. Wenn Du den Taster los lässt, wird der Timer auf "NULL" gestellt und die Uhr läuft im Minutentakt von der eingestellten Position weiter.

  • Mit dem Programm kann ich versuchen, kann aber etwas dauern, Was die Taster betrifft, ist so einer klein genug:

    Taster?


    Die sind ein bisschen teurer. Ich hatte mal billige von Amazon, aber die waren sch*§&!.

  • Das habe ich auch schon überlegt. Bei Tastendruck läuft das Testprogramm so lange bis die Uhr eingestellt ist. Wenn der Taster nicht gedrückt wird, läuft das Uhrprogramm.

    Auf die Sekunde genau kann man die Uhr mit dieser Anzeige sowieso nicht einstellen. Was meinst Du?

    Ich würde auch wieder auf den internen Pull up verdrahten. Denn der Taster zum Einbau hat ja auch nur 2 Anschlüsse. Kennst Du einen guten und Kleinen Einbautaster?

    Kannst Du mir helfen bei der Verknüpfung der Programme? In meinem geänderten Sketch lief bei Start des Testprogramm die Uhr auch nur eine Minute, wie im Loop programmiert. Das Testprogramm muss laufen, so lange der Taster gedrückt ist.

  • Kannst du nicht „einfach“ bei Tastendruck den Servo schneller drehen lassen um die Uhr zu stellen und wenn Du den Taster los lässt, läuft die Uhr in normalem Tempo weiter?

  • Die Lösung, nur den Kontakt zu benutzen mit einem internen Pull up habe ich nicht hinbekommen. Mein Sketch in #28 hat nicht funktioniert.

    Als ich mich mit dem Taster näher beschäftigt habe und das Schmierblatt gezeichnet habe, merkte ich, dass wenn ich auf den + Eingang vom Taster 5V verdrahte und S vom Taster auf den Eingangspinn verdrahte, dass immer der Widerstand 10K auf dem KY-004 Platinchen in Reihe liegt. (siehe mein Schmierblatt)

    Wenn nur der Taster vom KY-004 benutzt wird, muss 5V an - und S zum Eingangspinn. Leider hat Delivery nur eine Anschlussmöglichkeit gezeigt, mit externen Pull up. Das hat mich leider Stunden gekostet, aber ich habe das Innenschaltbild des Taster jetzt verstanden.

    Es gibt zwei Sketche, Programm und Test. Mit Tastendruck möchte ich mit Test #32 die Uhr einstellen bis die gewünschte Uhrzeit erreicht ist.

    Mit einem weiteren Tastendruck soll dann die Uhr gestartet werden. Ich hoffte, dass ginge mit einem Taster.

    Bevor ich das neue Gehäuse drucke, muss ich entweder einen Taster oder 2 Taster einzeichnen.