Ankündigung

Einklappen
Keine Ankündigung bisher.

ESP8266 KNX mit ETS

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Hi Thomas et al,

    dadurch, dass ich auf dem SAMD immer an die RAM-Grenzen komme, schaue ich mir ab und zu verschiedene Sachen an, derzeit eben GroupObject. Ich hab mal den Speicherverbrauch von einem KO angeschaut (Bytes in Klammern):
    Code:
           size_t asapValueSize(uint8_t code);
    (4)    GroupObjectUpdatedHandler _updateHandler;
           size_t goSize();
    (2)    uint16_t _asap = 0;
    (1)    ComFlag _commFlag = Ok;
    (4)    uint8_t* _data = 0;
    (1)    uint8_t _dataLength = 0;
    (4)    GroupObjectTableObject* _table = 0;
    (4)    Dpt _datapointType;
    Das wären 20 Bytes für ein KO an Verwaltungsdaten (ohne Nutzdaten), und da ein DPT auch noch 6 Byte groß ist und pro KO eine Instanz erzeugt wird, sind es sogar 26 Bytes, denke ich zumindest (ich bin nicht so fit in C/C++, korrigiert mich bitte, wenn ich hier falsch liege). Bei 256 KO sind das alleine schon 6656 Byte... das ist schon was.

    Könnte man nicht   GroupObjectTableObject _table = 0;  zu einer Klassenvariablen machen? Schließlich sind alle KO definitiv in der selben Table drin. Ich würde dann in meiner Logik nur noch für unterschiedliche DPT eine Instanz erzeugen (Factory) und so mit wenigen DPT-Instanzen auskommen. Das sind dann für die meisten KO schon 10 Byte ersparnis, man hätte dann nur 256 * 16 + n * 6 = 4096 + 6n (n ist die Anzahl unterschiedlicher DPT), also rund 2.5k gespart, was immerhin rund 7% des verfügbaren RAM ausmacht.

    Liege ich da komplett falsch oder ist der Gedanke gar nicht soooo blöd? Ich sage auch nicht, dass Du das machen sollst, ich würde mal wieder - bevor ich so was anfange - gerne die Sinn-Frage stellen .

    Gruß, Waldemar

    Kommentar


      Kann man bei Visual Studio Code eigentlich Defines for den Code angeben? Die Dateien für den RF werden z.B. nur bei MEDIUM_TYPE == 2 kompiliert. Ähnliches könnte man bei den Dateien für IP machen. Ich möchte ungern die Nutzer der Arduino-IDE aussperren. Mir ist aber nicht klar, wie man da derzeit für RF kompilieren kann. Wahrscheinlich gar nicht
      Hat da jemand eine zündende Idee?

      Kommentar


        Hi Thomas,

        was meinst Du mit
        Zitat von thesing Beitrag anzeigen
        Kann man bei Visual Studio Code eigentlich Defines for den Code angeben?
        Sicher nicht sowas
        Code:
        #ifdef __linux__
        #include <functional>
        typedef std::function<void(GroupObject&)> GroupObjectUpdatedHandler;
        #else
        typedef void (*GroupObjectUpdatedHandler)(GroupObject& go);
        #endif
        denn das hast Du ja schon häufig bei Dir drin. Ich kann bei VSCode dem Compiler aber sagen, dass __linux__ definiert ist (über compile-Flags). Meinst Du das?

        Gruß, Waldemar

        Kommentar


          Ich denke da könnte man in der Tat etwas sparen:

          (a)
          Code:
          (4) GroupObjectUpdatedHandler _updateHandler;
          Den könnte man los werden wenn man die UpdateHandler in der GroupObjectable verwaltet. (etwa stumpf als Liste) Die würde man dann bei einem Update durchgehen und die Callbacks aufrufen. Zusätzlich macht man noch ein globales Callback auf der GroupObjectTable (für Leute die viele GroupObjects haben und auf jedem ein UpdateCallback haben). Das registrCallback vom GroupObject wird dann gleich zur GroupObjectTable durchgereicht.

          (b)
          Code:
          (2) uint16_t _asap = 0;
          Der könnte auch weg. Dann müsste das GroupObject immer die GroupObjectTable nach dem Index von sich selbst fragen. Die müsste dann immer die Liste von allen GroupObjecten durchgehen und schauen wie der Index nun ist. Ich bin nicht sicher, ob man das will. Müsste man mal testen wie viel Performance das kostet. Wir haben ja immerhin kein 8bin MCUs.

          Code:
          (1) ComFlag _commFlag = Ok;
          (4) uint8_t* _data = 0;
          Die wird man wohl nicht los werden.

          (c)
          Code:
          (1) uint8_t _dataLength = 0;
          Den könnte man wohl analog zu _asap jedes mal von der GroupObjectTable herauspulen lassen.

          (d)
          Code:
          (4) GroupObjectTableObject* _table = 0;
          Der könnte wirklich zur Klassenvariable gemacht werden.

          (e)
          Code:
          (4) Dpt _datapointType;
          Hier wird es dir nichts Nützen nur eine Instanz pro Typ zu haben. Obwohl du die Größe dann von 6 auf 4 Byte drücken könntest. (ist ein struct aus drei shorts).
          Um den wirklich zu entfernen müsste man die value()-Methoden ohne Dpt entfernen.

          (a) und (d) finde ich gut.
          Für (b), (c) und (e) könnte man mit einem #define SAVE_SPACE oder SMALLER_GROUPOBJECTS o.ä. konfigurierbar machen. (Wenn das mit der Konfiguration per #define gescheit funktioniert.)

          VG
          Thomas

          Kommentar


            mumpf Genau das meinte ich. Wahrscheinlich ist es am besten eine config.h in die lib zu packen mit lauter #ifndefs. Die muss dann halt der arduino-IDE User editieren wenn er z.B. eine anderen Medientyp nutzen will. Oder hat noch jemand eine bessere Idee?

            Edit:
            Hm. Scheinbar sollte ein #ifdef MEDIUM_TYPE == FOO nicht nötig sein, das der Linker die nicht benutzen Objectdateien nicht linken sollte. Muss ich bei Gelegenheit mal testen.
            Zuletzt geändert von thesing; 29.10.2019, 00:16.

            Kommentar


              Hi Thomas,

              Zitat von thesing Beitrag anzeigen
              Scheinbar sollte ein #ifdef MEDIUM_TYPE == FOO nicht nötig sein, das der Linker die nicht benutzen Objectdateien nicht linken sollte.
              das habe ich irgendwo auch mal gelesen, es aber bisher nicht verifiziert. Ich muss mir mal die Kommandozeile für den Linker ansehen.

              Aber selbst wenn der Linker nur die benötigten object files linkt, sollte man über #ifdef/#ifndef absichern, dass man nicht versehentlich oder aus Unwissenheit Codingteile referenziert, die nicht gebraucht werden. Die Idee einer config.h, die überall includiert ist, finde ich gut (wenn man bei Arduino wirklich keine defines von außen mitgeben kann - ich dachte, das kann jede C/C++ Entwicklungsumgebung). Allerdings hätte ich in dem config.h nur ein paar #defines gesehen, die #ifdef/#ifndef stehen doch dann im restlichen Stack, oder?

              Ahh, jetzt hab ich es gepeilt! Du würdest config.h folgendermaßen aufbauen:
              Code:
              #ifndef MEDIUM_TYPE 
              #define MEDIUM_TYPE 0
              #endif
              So könnte man sowohl von außen konfigurieren (über Kommandozeile) wie auch über die config.h in der Arduino-IDE. Wäre super für eine Konfigurierbarkeit, da könnte dann auch Dein Vorschlag von oben mit   #define SMALLER_GROUPOBJECTS  rein.

              Jetzt, wo ich es versanden habe (korrigier mich, falls nicht):

              Gruß, Waldemar

              Kommentar


                Hi Thomas,

                Zitat von thesing Beitrag anzeigen
                Ich denke da könnte man in der Tat etwas sparen:
                danke für Deine ausführliche Antwort, ich muss nur mal ein paar Sachen mit meinen Worten formuliert nachfragen, um zu checken, ob ich Dich richtig verstanden habe...

                Erstmal um mein Unwissen auszuräumen und was zu lernen:
                Punkt (e): Dpt ist doch eine Klasse mit 3 short, also 6 Byte lang. Ich dachte, wenn man eine Instanz kreiert, dann wird die dynamisch in Speicher erzeugt (6 Bytes) und die Referenz darauf (also   _datapointType ) ist technisch ein Pointer, also weitere 4 Byte. Mit meinem Vorschlag wollte ich die Referenz belassen, aber die immer gleichen Instanzen (z.B. 120-mal DPT(1,1)) vermeiden. Ich ging davon aus, ich würde dann nur 6 statt 120*6=720 Bytes verbrauchen. Natürlich hätte man immer noch 120*4 Byte referenz auf diese Instanz. Um beim Beispiel zu bleiben: Vorher sind es 120*(4+6)=1200 Byte, hinterher 120*4+6=486 Byte. Du sprichst vom struct, den habe ich aber nirgendwo gesehen. Als struct wären es dann ja 120*6=720 Byte, da man sich die ganzen Pointer spart. Wie gesagt, diese Rückfrage ist nur, um mein Bild zurechtzurücken...

                Ansonsten habe ich Deine Antwort so verstanden, dass man komplett _datapointType weglassen könnte, wenn man alle Methoden, die darauf verweisen (eben die erwähnten value-Methoden) entfernt. Was bedeutet, dass man beim Aufruf immer den DPT mitgeben muss. Ich schau mir das an, aber ich könnte mir ein #define vorstellen, das den _datapointType und alle davon abhängigen Methoden ausblendet. Das wären dann (je nachdem, ob ich oben richtig liege) 6 oder 10 Bytes weniger, dafür weniger Komfort beim Aufruf.

                Punkt (d) sind wir uns ja einig, Punkt (c) würde ich nicht anfassen wollen (für nur 1 Byte). Punkt (b) müsste auch nicht sein, wenn man Punkt (a) hinbekommt.

                Zu Punkt (a) aber auch noch eine Verständnisfrage:
                Zitat von thesing Beitrag anzeigen
                Zusätzlich macht man noch ein globales Callback auf der GroupObjectTable (für Leute die viele GroupObjects haben und auf jedem ein UpdateCallback haben)
                Den Teil verstehe ich: Nur ein Callback, da das KO ja als Parameter mitgeschickt wird, muss man im callback sowieso anhand der asap dispatchen. Das wäre neu, keine Seiteneffekte.

                Zitat von thesing Beitrag anzeigen
                Den könnte man los werden wenn man die UpdateHandler in der GroupObjectable verwaltet. (etwa stumpf als Liste) Die würde man dann bei einem Update durchgehen und die Callbacks aufrufen.
                Hier habe ich ein Problem: Entweder man muss dann in der Liste das Pärchen (asap, callback) halten oder wieder ein Array von callbacks machen, was keinen Vorteil bringt. Und einfach stumpf alle callbacks aufzurufen würde eine Verhaltensänderung mit sich bringen, denn zur Zeit wird ja nur das KO aufgerufen, dass einen Callback zugewiesen hat. Die Liste (asap, callback) würde letztendlich mehr Speicher verbrauchen, sobald mehr als 2/3 aller KO einen callback haben.

                Mein Vorschlag: Über ein #define den callback aus dem GroupObject entfernen und dann nur noch den globalen callback zu lassen. Dispatching passiert dann auf Applikationsebene und alles ist gut. Das wären dann weitere 4 Bytes gespart.

                Ich würde dann auf sowas hin arbeiten:
                Code:
                    size_t asapValueSize(uint8_t code);
                #ifndef SMALLER_GROUPOBJECTS
                    GroupObjectUpdatedHandler _updateHandler;
                    Dpt _datapointType;
                #endif
                    size_t goSize();
                    uint16_t _asap = 0;
                    ComFlag _commFlag = Ok;
                    uint8_t* _data = 0;
                    uint8_t _dataLength = 0;
                    // GroupObjectTableObject* _table = 0; //is now class variable
                natürlich mit #ifndef an allen weiteren abhängigen Stellen + ein globaler _updateHandler. Damit wäre ein KO nur noch 5 Byte (+Daten) groß, oder?

                Gruß, Waldemar

                P.S.: Wäre der globale _updateHandler nicht als Klassenmethode im GroupObject besser aufgehoben? Anstatt in der GroupObjectTable?

                Kommentar


                  Hi!
                  Ich würde auch für eine globale config.h plädieren.
                  Projekte wie FreeRTOS und BTstack nutzen sowas auch, um internes Feintuning usw. für den Stack einzustellen.

                  (Die Arduino GUI ist für mich keine IDE ;-) )

                  Ich habe PlatformIO mit VS Code zum ersten Mal für die KNX-RF Implementierung genutzt und bin begeistert.
                  Auch das Debugging in VS Code mit einem Segger J-Link EDU klappt super und hat echt geholfen.
                  Die #defines setzt man dort in der zentralen Datei "platformio.ini".

                  Der Arduino Build Prozess ist beschrieben.

                  Für die Linux-Plattform könnte man ja das CMakeFileLists.txt entsprechend anpassen, um #defines unterzubringen.


                  ABER: einfacher ist vermutlich wirklich eine einfache globale config.h.
                  Da könnte ich dann auch die PIN-Definitionen für RF unterbringen. Und welches SPI Device zu nutzen ist.

                  Kommentar


                    Hi Nanosonde,

                    ich bin von PlatformIO auch begeistert und habe mir gestern den Segger J-Link EDU bestellt. Falls ich das nicht vernünftig eingerichtet bekomme, würde ich mich nochmal an Dich wenden

                    Zitat von Nanosonde Beitrag anzeigen
                    einfacher ist vermutlich wirklich eine einfache globale config.h.
                    Ich fände die Möglichkeit, beides zu nutzen, am schönsten (also Thomas #ifndef-Vorschlag).

                    Gruß, Waldemar

                    Kommentar


                      PlatformIO und J-Link war einfach einzurichten. Ich nutze das alles unter Windows 10 64-Bit.
                      Ich kann Dir meine platformio.ini Datei hier mal zur Verfügung stellen, wenn Du magst.

                      Zitat von mumpf Beitrag anzeigen
                      Ich fände die Möglichkeit, beides zu nutzen, am schönsten (also Thomas #ifndef-Vorschlag).
                      Ich würde die config.h jetzt nicht unnötig mit #ifndefs usw. zupflastern.
                      Derjenige, der das vom Build Prozess aus steuerbar benötigt, kann sich das ja einfach selbst mit zwei Zeilen hinzufügen.
                      Von daher würde ich dort wikrlich nur die #defines mit ein wenig Doku wozu das ist, da rein packen.

                      Kommentar


                        mumpf
                        Der Dpt ist im GroupObject derzeit nicht als Referenz (Dpt&) definiert. Damit entspricht die Größe der Summe der Instanzvariablen. In c++ ist eine struct eine Klasse nur public membern. Bei mir ist der Dpt ein Klasse mit nur public-Membern. Also praktisch das gleiche, aber nicht so deklariert.

                        Es würde schon Sinn machen jeden Dpt nur einmal zu instanzieren. Da hast du Recht.

                        Bei Punkt (a) müsste man in der GroupObjectTable schon eine Liste von (asap, callBack) haben um die richtigen Callbacks ermitteln zu können.

                        Vielleicht sollten wir für Listen entweder die STL nutzen, oder ein eigenes Template hinzufügen.

                        Da niemand ein Problem mit einer config.h hat, wird es dann darauf hinaus laufen. Allerdings mit #ifndefs damit man das bei einer "richtigen" IDE übersteuern kann.

                        Kommentar


                          Nanosonde:
                          Ich kann Dir meine platformio.ini Datei hier mal zur Verfügung stellen, wenn Du magst.
                          Das wäre toll, danke.

                          Gruß, Waldemar

                          Kommentar


                            Zitat von thesing Beitrag anzeigen
                            Der Dpt ist im GroupObject derzeit nicht als Referenz (Dpt&) definiert
                            Danke für die Aufklärung. Auf das & hab ich nicht geachtet. Das zeigt mal wieder meine C++-Lücken...

                            Zitat von thesing Beitrag anzeigen
                            Es würde schon Sinn machen jeden Dpt nur einmal zu instanzieren. Da hast du Recht.
                            Wenn das nur 4 statt 6 Bytes bringt, ist das nicht so doll... da macht Dein Vorschlag mit dem weglassen des _datapointType und immer DPT mitgeben beim Aufruf der Value-Funktionen mehr Sinn.

                            Zitat von thesing Beitrag anzeigen
                            Allerdings mit #ifndefs damit man das bei einer "richtigen" IDE übersteuern kann.
                            Da bin ich auch für, das man das dann einfacher in .git einchecken und muss es mit einer "richtigen IDE" nicht anfassen .

                            Gruß, Waldemar

                            Kommentar


                              Aktuell schreibe ich beim Save am Anfang des Flashs ein paar Magic Numbers um beim Restore nach einem Restart zu erkennen ob der Speicher gültig ist. Zukünftig würde ich dort die PID_HARDWARE_TYPE und PID_VERSION vom DeviceObject schreiben wollen. Dann würde man zusätzlich prüfen, ober der Flash zum Sketch passt. Das setzt natürlich vorraus, dass man die Werte des DeviceObjects im Sketch entsprechend füllt. Was haltet ihr davon?

                              VG
                              Thomas

                              Kommentar


                                Hi Thomas,

                                ich würde ja gern was davon halten, aber ich weiß nicht, worauf Du Dich beziehst. Um welche Attribute welcher Elemente im XML geht es? Geht es um diese beiden?
                                Code:
                                        <ApplicationProgram Id="M-00FA_A-..." ApplicationNumber="555" ApplicationVersion="11" ProgramType="ApplicationProgram" ...>
                                Wenn ja, dann bitte nur auf ApplicationNumber prüfen, nicht auf die ApplicationVersion! Ich verlasse mich ja explizit drauf, dass man über ReplacesVersions die Version kompatibel erhöhen kann ohne die Firmware ändern zu müssen. Da könnte man höchstens eine MinVersion prüfen. Aber wahrscheinlich geht es um was anderes, oder?

                                Grundsätzlich bin ich dafür, dass man prüft, ob die Firmware (also der Sketch) zu einer knxprod passt, aber die vorgesehenen Upgradepfade (neue knxprod für bestehende Firmware) sollen immer noch gehen.

                                Ich hab das bisher selber geprüft, indem ich den ersten Paremter in der ETS unsichtbar, mit einer konstante (magic value) vorbelegt immer mitschicke und in der Firmware prüfe, ob der erste Parameter dieses magic value enthält.

                                Gruß, Waldemar

                                Kommentar

                                Lädt...
                                X