Beiträge von KaiR

    Ich glaube da wird etwas durcheinander geschmissten. In der Referenz zu analogRead() ist von max. 10.000 Lesevorgängen pro Sekunde die Rede. Das ist nicht das gleiche wie die empfohlene Frequenz für den ADC, die zwischen 50kHz und 200kHz liegen soll.


    Du kannst die Abtastung aber für Deine Ansprüche selber programmieren. Mit den Registerwerten ADPS0 bis ADPS2 des Registers ADCSRA kann ein Vorteiler eingestellt werden, der die Samplingrate des ADC beeinflusst. Der maximale Teiler beträgt 128. Wird der µController, wie das bei den Nanos und Unos üblich ist mit 16Mhz betrieben, ergibt das eine Frequenz von 125kHz. Der ADC braucht 13 Takte für eine Wandlung. Das wären dann grob 9600 Samples pro Sekunde (125.000 / 13), bzw. du könntest 9600 Werte pro Sekunde einlesen (siehe Lesevorgänge oben).


    Aber eigentlich muss ich das gar nicht mehr alles schreiben, weil es steht hier gut beschrieben wie das funktioniert. Im Prinzip steht da auch schon der Code den Du (vermutlich) brauchst.


    Wenn Du mehr über die Timer von AVR-Controllern wissen möchtest, kann ich nur die Beträge von Wolles-Elektronikkiste empfehlen:

    https://wolles-elektronikkiste.de/timer-und-pwm-teil-1

    https://wolles-elektronikkiste…-pwm-teil-2-16-bit-timer1


    Auf den Seiten wird zwar viel über PWM geschrieben aber die Mechanismen die man braucht, um in bestimmten Zeitabständen eine Aktion auszuführen, sind dort auch beschrieben ("normal Mode").

    Was für einen Sensor? Wenn es um das Auswerten von Analogwerten geht, dürfte eher der AD-Wandler statt eines Timers gefragt sein. Die Abtastrate des AD-Wandlers wird über spezielle Register des Wandlers eingestellt. Evtl. reicht es auch einfach die analogRead() vom Arduino Framework zu verwenden.


    Aber das nur allgemein. Für eine konkrete Antwort ist Deine Frage zu unkonkret.

    CANDO : Wenn man einen Code kopiert weil man hofft, dass er die Lösung für das vorhandene Problem ist, sollte man auch mal schauen was für Bibliotheken dort verwendet werden und was die so können. Erst dann kann man doch beurteilen, ob damit eine Lösung möglich ist oder nicht.


    Hast Du Dir denn mal die Hilfe zu der verwendeten Webserver Bibliothek angeschaut?


    https://github.com/me-no-dev/E…bServer#espasyncwebserver


    Dort steht unter anderem, dass der Server ein AsyncEventSource Plugin hat, mit dem der Server Textnachrichten an einen Client schicken kann. Außerdem ist dort beschrieben, wie Server und Client funktionieren:

    https://github.com/me-no-dev/E…async-event-source-plugin.


    Das Ganze nennt sich HTML SSE (Server Sent Events) und wir sind, wie ich bereits vermutete, bei HTML5 und Javascript

    https://www.codingblatt.de/html5-server-sent-events/.


    Damit könnte sich etwas implementieren lassen, was Deinem Plan nahe kommt.


    Nun wird jedoch klar, warum der Kollege den Du angeschrieben hast, keine Zeit hat…. ;). Du darfst Dich jetzt nämlich mit mehreren Dingen beschäftigen (mindestens):

    • Die Funktionsweise der zu verwendenden Bibliotheken verstehen,
    • Die Erstellung der Webseite(n) (die Du beim ESP32 / 8266 auch im SPIFF Speicher ablegen kannst),
    • HTML 5 (SSE),
    • Javascript (auch Bestandteil der Webseiten),
    • die Implementierung des Codes.

    Wenn man das alles noch nicht gemacht bzw. noch nicht kennt, eine sehr anspruchsvolle, zeitintensive Aufgabe. Selbst dann, wenn man sich mit den Themen schon mehr oder weniger auskennt, so etwas aber nicht andauernd umsetzt und man tief im Thema drin ist.



    Was die Steuerung des Rolladens betrifft, gibt es aus meiner Sicht zwei kritische Punkte:

    Wenn der Rolladen ganz oben ist (oberer „Anschlag“).

    Wenn der Rolladen ganz unten ist (unterer „Anschlag“).


    Um diese Positionen festzustellen fallen mir auf die Schnelle folgende Möglichkeiten ein:

    • Die schon angesprochenen Reedkontakte (Neodyn Magnete sind sehr stark). Minimum wäre ein Magnet und zwei Kontakte (oben, unten)
    • Evtl. Hall-Sensoren statt Reedkontakte (sind empfindlicher für Magnetfelder)
    • Mikroschalter
    • Lichtschranke
    • Positionsermittlung über die Anzahl der Kurbeldrehungen

    Letzteres würde eine Kalibrierung benötigen und auch eine Mimik, die die Positionen dauerhaft speichert (Stromverlust, Aus-, Einschaltung). Allerdings wäre keine Installation am Rolladen notwendig.

    Da das HTTP-Protokoll transaktionsorientiert ist, dürfte sich die Geschichte „Der Rolladen bewegt sich, solange der Button gedrückt ist (Totmannknopf)“ nicht ohne weiteres umsetzen lassen. Nur Button wurde gedrückt -> Rollladen bewegt sich. Button wird ein weiteres mal gedrückt -> Rolladen stoppt, funktioniert. So, wie es im geposteten Quellcode erfolgt.


    Beim Http-Protokoll erfolgen die Anfragen vom Client. Dieser erzeugt Requests, die vom Server beantwortet werden. Ist der Request bedient, kappt der Server die Verbindung wieder.


    Die Prüfung „ob der Client noch lebt“ muss demnach quasi vom Client selber kommen. Es müsste eher heißen, kann der Client den Server noch erreichen und reagiert dieser noch. Also ist der Ansatz eher schlecht, weil wenn der Server nicht mehr reagiert, nimmt er auch keine Steuerbefehle mehr an.


    Der Server (also der Micocontroller) könnte jedoch auf einer anderen Schicht, über den TCP-Socket, prüfen ob ein Client noch erreichbar ist.


    Evtl. lässt sich z.B. mit Javascript so etwas wie der Totmannknopf-Mechanismus umsetzen. Allerdings muss ich zugeben, dass mein Wissen um die Webprogrammierung veraltet ist. Evtl, gibt es ja noch Kniffe z.B. über HTML5 die ich nicht kenne. Das müssen Leute beantworten, die sich da besser auskennen.


    Was genau man alles machen kann wird auch von der Implementierung der ESPAsyncWebServer Bibliothek abhängen. Z.b. das Setzen von Cookies für die Clienterkennung.


    Der „Zerstörung“ des Rolladens könnte man ja durch entsprechende Reedkontakte am Rolladen oder einer elektronischen Kontrollmimik der Hebe-/Senkvorrichtung des Rolladens entgegenwirken.


    Jetzt habe ich viel geschrieben und konnte Dir nicht wirklich helfen, aber vielleicht waren ja ein paar Stichworte dabei, die bei Deiner Suche nach einer Lösung nützlich sind.

    @Pius: Basierend auf Deinem PROGMEM Beispiel habe ich meine Version mit den Formatstrings angepasst.


    https://wokwi.com/projects/334369257099887188


    Leider muss man den Formatstring, wenn man ihn mit F() als Parameter übergeben will, immer nach (char*) bzw. (const char*) casten (geht beides). Ich habe deshalb ein Makro CF definiert um Tipparbeit zu sparen.


    Wird nicht gecastet, erhält man die Fehlermeldung :

    Zitat

    error: cannot convert 'const __FlashStringHelper*' to 'const char*' for argument '1' to 'void serialPrint(const char*, ...)'

    Deshalb müssen die Formatstrings mit CF("Formatstring") statt mit F("Formatstring") übergeben werden. Vielleich weißt Du ja wie man das noch besser machen kann.


    Ansonsten funktioniert das ganz gut und der Speicherverbrauch ist auch in Ordnung. Da ja RAM i.d.R. der knappste Faktor bei µControllern ist, kann sich Formatstring-Variante auch lohnen:

    RAM: [= ] 9.6% (used 196 bytes from 2048 bytes)

    Flash: [= ] 13.2% (used 4070 bytes from 30720 bytes)


    Diese Variante ist zwar flexibler, aber logischerweise nicht so sparsam wie die ohne Formatstrings. Aber alle Beispiele verbrauchen weniger Ressourcen als die "String Klassen" Geschichte.


    Die Erweiterung einer Ausgabe auf ein Display ist möglich und sicher auch sinnvoll. Man muss nur aufpassen, wie ich in #17 geschrieben habe, das Zeichen die nicht in der standard ASCII Tabelle < 0x7F abgebildet werden als UTF-8 Zeichen gespeichert werden.


    Da diese bis zu vier Byte lang sein können, ist bei Ausgaben, die eigentlich nur auf ein Byte Zeichen abzielen, ein etwas erhöhter Aufwand nötig, wenn diese „Sonderzeichen“ korrekt dargestellt werden sollen.


    Ich freue mich jedenfalls über Deine Beiträge. Ich lerne jedes mal etwas dazu :):thumbup:.

    Sehr schön Pius.


    An folgendem kann man sehen, wie effektiv bei Deiner Variante mit pgm_read_byte_near() RAM "freigeschaufelt" wird:


    RAM: [= ] 9.2% (used 188 bytes from 2048 bytes)

    Flash: [= ] 7.6% (used 2336 bytes from 30720 bytes)


    ca. 63% weniger RAM Verbrauch als bei der Urversion mit "String"

    Hans: ich habe mal deinen Quellcode "Neue-Zeichenketten2.ino" vom Posting #10 aufgenommen und es mal als Beispiel mit reinen C-Strings umgesetzt.


    Diese Variante ist aber ohne das "PROGMEM" Extra, was in der vorgestellten Form auch etwas schwerer umzusetzen ist.


    Kernstück ist eine C-Funktion mit variabler Parameterliste.

    Das sieht so aus: serialPrint(true, pTxt[STEP], pTxt[ZAHL], " soll", NULL);


    Beim Gebrauch dieser Funktion ist folgendes zu beachten:

    Der erste Parameter muss immer angegeben werden. Er ist entweder "true" oder "false". Wird "true" übergeben, so wird beim letzten Wort der Liste ein Zeilenumbruch mit ausgegeben (wie bei Serial.println), wird "false" übergeben erfolgt das nicht (also wie Serial.print). Im Anschluss an diesen Wert beginnt die variable Parameterliste, der im Prinzip (RAM abhängig) beliebig viele Textpointer übergeben werden können. Wichtig ist, dass der letzte Übergabeparameter IMMER! NULL sein muss. Das ist notwendig, um das Ende der Parameterliste erkennen zu können. Wird das vergessen, wird es komisch.


    Das Programm: https://wokwi.com/projects/334201469761749587


    Wenn man dein Programmbeispiel compiliert (Nur mit den String Klassen BezA und BezB. Alles andere wurde gelöscht) liegt der Speicherverbrauch bei

    RAM: [== ] 25.0% (used 512 bytes from 2048 bytes)

    Flash: [= ] 14.5% (used 4468 bytes from 30720 bytes)


    Bei dem oben verlinken Beispielprogramm sieht es wie folgt aus:

    RAM: [== ] 17.5% (used 358 bytes from 2048 bytes)

    Flash: [= ] 7.5% (used 2312 bytes from 30720 bytes)


    Also der Speicherverbrauch ist mit C-Strings deutlich geringer (≈ -30% beim RAM und ≈ -48% Flash Speicher) als bei der Verwendung von "String". Und du kannst davon ausgehen, dass zur Laufzeit auch weniger RAM verbraucht wird. Die Programme wurden für einen Arduino Nano compiliert.


    Eine schickere Variante gibt es hier: https://wokwi.com/projects/334256716602409554


    Dabei funktioniert die Ausgabe analog zu sprintf() in vereinfachter Form. Dadurch, dass die Parameterzahl durch die Formatierungszeichen %c, %d oder %f angezeigt wird, entfällt die Übergabe von NULL beim Funktionsaufruf. Die boolsche Variable für den Zeilenumbruch ist nicht mehr notwendig, weil dieser durch die Eingabe von "\n" im Formatstring erzeugt werden kann.


    Dass, im Gegensatz zur ersten Variante, verschiedene Datentypen übergeben werden können ist ebenfalls ein Vorteil. Allerdings verbraucht die Funktion printSerial() natürlich etwas mehr Speicherplatz als die erste Variante. Außerdem belegen die Formatstrings auch wieder Platz. Dennoch ist das Programm immer noch sparsamer als die Sting Klassenvariante, obwohl noch zusätzlicher Text zu Demonstrationszwecken ausgegeben wird:

    RAM: [== ] 22.6% (used 462 bytes from 2048 bytes)

    Flash: [= ] 13.0% (used 3982 bytes from 30720 bytes)


    Das soll es dann aber dazu gewesen sein, sofern keine neuen Fragen auftauchen.

    Inzwischen gibt es auch ein PCB für die Funkuhr. Man kann die einzelnen Komponenten gleich aufstecken oder auch per Kabel verbinden.

    Ein serieller Anschluss für evtl. Debugging-Ausgabe oder sonstiges ist auch vorhanden.


    dcf77-rtc-pcb-picture.jpg



    pcb-front.jpg


    pcb-back.jpg


    @Pius: so konnte ich die ersten Kondensatoren aus Deiner Sendung verwenden :). Die reichen noch eine Weile ^^.


    Die KiCad-Dateien gibt es auf Github.

    Es entstehen nie zu viele oder zu wenig K-zähne weil sie nicht von den Rundungsfehlern abhängen, sondern nur vom errechneten Gesamtfehler. Das habe ich ja versucht zu erklären. Die Nachkommastellen haben lediglich Einfluss auf den Abstand von den Korrekturzähnen. Diese Nachkommastellen verteilen sich aber auf den Gesamtumfang. darum gibt es beispielsweise mal zwei Regelzähne einen Korrekturzahn, drei Regelzähne einen Korrekturzahn dann wieder zwei Regelzähne oder es gibt 3,3,4 er Gruppen usw.


    Wenn Du dir das Programm mal anschaust, wirst du nicht eine double oder float Variable finden. Es werden nur (gerundete) Ganzzahlen verwendet. Das Aufrechnen von Nachkommastellen macht das Ganze nur unnötig kompliziert.,


    Ich habe es jetzt nicht getestet, aber das Programm wird auch bis 500 Zähne funktionieren.

    Nach meinem Verständnis ist der ganze Sermon ist doch gar nicht nötig, weil die Werte rechnerisch ermittelt werden. Somit müssen die Korrekturzähne pro Zahnrad immer eine identische Größe haben, sie können nicht direkt aufeinander folgen. Der Korrekturwert für einen Korrekturzahn ist im Beispielprogramm fest auf einen Step eingestellt. Es wird entweder -1 oder -(-1) gerechnet. Je nachdem ob der Gesamtfehler positiv (zu viele Steps) oder negativ (zu wenig Steps) ist. Aus dem Gesamtfehler ergibt sich die Aufteilung über den Kreisumfang aufgrund der Gesamtsteps.


    Das heißt, wenn ich ein Zahnrad habe, dessen Berechnung am Ende beispielsweise 20 Steps zu viel ergibt, müssen diese überzählige Steps gleichmäßig über den Kreisumfang abgezogen werden um am Ende auf die Gesamtstepzahl, die richtige Anzahl Zähne und eine mögl. gute Symmetrie zu kommen. Es ergibt sich ja schon aus der Rechnung Gesamtstepzahl / 20, wieviele Steps zwischen zwei Korrekturzähnen liegen müssen um diese gleichmäßig aufzuteilen. Ergibt die Berechnung zu wenig Steps, gilt das Gleiche nur dass dann ein Korrekturzahn einen ( -(-1) )Step mehr als ein Regelzahn hat


    Darum passt am Ende auch immer die Gesamtstepzahl. Das lässt sich ja auch am Wert (Step pro Zahn) bei der Programmausgabe ablesen. Da kann es gar keine Überläufe oder so etwas geben.


    Naja … lassen wir es dabei. Kann ja durchaus sein, dass da noch einige Aspekte sind, die ich nicht kapiere, aber ich baue das Ding ja auch nicht :). Ich habe hier nur versucht zu erklären, welcher Algorithmus hinter dem Beispielprogramm steckt.

    Ich habe heute auf der Terrasse beim relaxen nochmal das Programm welches ich seinerzeit als Beispiel unten verlinkt habe angefasst eine kleine Änderung vorgenommen und es so programmiert, dass immer mit einem Korrekturzahn angefangen wird. Diese werden gleichmäßig auf eine komplette Drehung verteilt. Es gibt nie mehr als einen Step-Differenz zwischen zwei aufeinanderfolgenden Zähnen. Die Gesamt-Stepzahl von 2048000 Steps wird immer erreicht. Nie unter- oder überschritten.


    Die Verteilung entspricht dem, was Dein Programmcode auch so "ausspuckt".


    https://wokwi.com/projects/333730702599127635

    Hans: Was mir noch eingefallen ist... Bezüglich der Sonderzeichen musst Du aufpassen. Die Codes, die für die serielle Ausgabe gelten, sind nicht unbedingt die gleichen, die auch am Display verwendet werden. Du hattest geschrieben, dass Du das 16x4 I2C LCD Display verwenden willst.


    Wenn Du möchtest, dass auf dem Display Eszett oder Umlaute dargestellt werden, musst Du für Deine Strings die Codes gemäß der ASCII Tabelle des Datenblattes vom Display verwenden oder selber Zeichen definieren und diese in das Display programmieren:


    Ein Beispiel für beides: https://wokwi.com/projects/333659154160812626

    Das Beispiel zeigt, wie man Zeichen selber definiert, sowie die Ausgabe auf einem 16x2 LCD Display und die Umwandlung von UTF-8 Zeichen in den LCD spezifischen Zeichensatz.


    In dem Beispiel habe ich ein kleines ü und das € Zeichen definiert und auf das Display übertragen. Die Zeichen werden an die Speicherstelle 1 und 2 des Displays geladen und entsprechend auch mit dem Code 0x01bzw. 0x02 angesprochen. Komischerweise ist in der Standardtabelle des Displays zwar ein kleines ä und ö definiert aber nur ein großes Ü. Darum habe ich, um das Prinzip zu zeigen, noch ein kleines ü und ein € - Zeichen selbst "gebaut".

    Empfehlenswert ist noch das „C++ Buch, das umfassende Handbuch“ von Thorsten T. Will vom Rheinwerk Verlag.


    Es ist vor allem auch deshalb empfehlenswert, weil darin noch erklärt wird was guten Code ausmacht (Sicherheit, Qualität und Nachhaltigkeit), RAII Mechanismen, Solid ( klassisches Objektorientiertes Design) usw.. Ich habe auch das „Der C++ Programmierer“ Buch von Ulrich Breymann (Hanser Verlag) im Schrank, allerdings finde ich erstgenanntes Buch besser. Das aber nur am Rande. Wie Pius schon erwähnt hat, sollte man die grundsätzlichen Mechanismen von C beherrschen bevor man mit C++ tiefer einsteigt.


    Und sich die objektorientierte Denke anzueignen, dauert auch einige Wochen intensive Beschäftigung mit dem Thema. Um darin dann noch gut zu werden braucht es viel Praxis und Monate bis Jahre ;).


    Bei Mikrocontrollern ist der C++ Umfang im Vergleich zur Computerprogrammierung aus guten Gründen abgespeckt, aber wenn man die vielfach vorhandenen Bibliotheken in der „Arduino“ Welt verstehen will, kommt man auch um C++ nicht herum.


    Es ist auch empfehlenswert sich mal nicht mit der „Funktionsweise“ einer speziellen Programmiersprache zu beschäftigen, sondern auch mit der Code Erstellung im allgemeinen. Wie programmiert man pflegeleichten und leicht erweiterbaren Code. Darum auch mein oben stehender Buchtipp.


    Sehr interessant ist auch dieses Werk: Objektorientierte Programmierung.


    Dieses Buch kann man online lesen. Es ist aber auch harter Tobak:). Zumindest für Hobbyisten…

    Was dort geschrieben steht, lässt sich aber auf eine Vielzahl von Programmiersprachen übertragen (Java, C++, Python, Ruby usw. usw.)

    Da Du meine "Warnung" zur Nutzung der Klasse String scheinbar in den Wind schießt, schau Dir doch mal Renés die Ausführungen zur String Klasse an. Höre auf die Warnungen ab 1:35 Minute. Und die Ausführungen ab der 20. Minute sind in dem Zusammenhang hilfreich und sollten von Dir beachtet werden, wenn du denn nun unbedingt diese "verzarkte" String Klasse in ausgiebigem Maße verwenden willst.



    Sollte dein Programm mal nicht mehr so arbeiten wie du es erwartest, könnte es mit den genannten Nachteilen der Klasse zusammenhängen... Dann viel Spaß bei der Fehlersuche :).


    Mit C-Strings braucht man sich diese Gedanken nicht machen. Aber vielleicht hast Du ja Glück und/oder genug Platz.

    Vorsicht mit der Zeile:

    char TextA[8] = {'h', 'e', 'i', 's', 's', 'e', 'n'};


    das Array TextA ist 8 char gross dimensioniert und danach mit 7 chars aufgefüllt worden.

    Bei TextA[7] (der 8. Eintrag) ist der Inhalt undefiniert.


    Da hat der Pius recht... dabei könnte es in der letzten Ausgabezeile (Zeile30) auch mal Textsalat oder einen Absturz geben.

    char TextA[8] = {'h', 'e', 'i', 's', 's', 'e', 'n', '\0'};

    wäre da korrekter. Zumindest kann dann bei einem Serial.print(TextA); nichts passieren.


    Wenn man das TextA - Array als Array mit einzelnen Feldern TextA[0..6] sieht und NUR über einen Index von 0 bis 6 darauf zugreift, kann man es auch so lassen. Beispiel:


    for (byte i=0;i<7;i++) {

    Serial.print(TextA[i]);

    }


    Aber C-Strings MÜSSEN IMMER mit einer 0 terminiert sein. Und die Variable TextA ohne Index [] ist die Startadresse eines C-Strings.


    Bei einer Definition wie TextA[] = "heissen"; erfolgt die Null-Terminierung implizit durch den Compiler. Nicht aber, im obigen Beispiel.


    Aber wie ich sehe, hast Du ja genug Literatur.... Das reicht für ne Weile :).

    Wenn du einen String im Code als Literal verwendest, so verbraucht er bei jeder Anwendung im Code Speicherplatz. Im Prinzip hast Du also recht, dass weniger Speicherplatz gebraucht wird, wenn du drei Variablen mit Text definierst und diese dann mehrfach eingesetzt werden.


    Allerdings solltest Du es nicht so machen, wie Du beschrieben hast. Es ist nicht wirklich empfehlenswert bei einem Microcontroller mit wenig Speicherplatz die "String" Klasse zu verwenden. Das ist zwar eine Klasse, die bequem zu verwenden ist und es (Einsteigern) erleichtert mit Strings umzugehen, allerdings ist sie nicht ressourcenschonend.


    Es sollte sämtlicher Text in C - Textarrays gespeichert werde und die Zeiger auf diese Arrays (Zeiger = die Adresse des Textes im Speicher) wiederum in einem weiteren Array. Dieses weitere Array wird dann zur Textausgabe an Funktionen oder Methoden wie Serial.println() übergeben. Das verbraucht dann nur noch zwei Byte. Egal wie lang der auszugebende Text ist, weil nur noch mit Zeigern gearbeitet wird.


    Ein Beispiel. Oder noch eines mit OLED Display Beispiel.


    Eine etwas aufwändigere Variante (jedoch gleiches Prinzip), die den Text nicht im RAM sondern im Flash-Speicher speichert ist hier beschrieben: Progmem.

    HIer ist das zweite Beispiel relevant "Array von Strings".