Making this code smaller... But how?

Hi
I’m not very experienced with this, but I have really tried to make this sketch smaller as good as I could.
It’s a sketch that reads two different kinds of sensors, sends those values to a KNX bus, reads other values from that KNX Bus and prints all kinds of values to a E-ink display.

I got rid of an extra font for smaller letters, used functions (did not save any space) tried to use integers where I could and I just don’t get any smaller.

I’m at about 33800 Bytes now 110%. It has to run on a pro Mini (or pro Micro).

Any hints appreciated. I’m not experienced nor brave enough to work on the libraries…

The code is about 11000 characters and I can’t post it inline here. Thus the attachment. Sorry about that.

KNX_EPD_Display_Sensors.ino (10.6 KB)

in several cases, you have sequences of sub-function calls to the same sub-function with different argument. each sub-function call requires a bunch of code. arguments can be captures in tables and a single function used to call that sub-funciton (e.g. WriteEPDFloat()) with those arguments. All the existing sub-function calls are replaced with 2 function calls.

in maintainKnxSerial(), having a table of pointers to the variables being written to, where an index to the table is used to locate it, could replace most of those if/else ifs with a single statement (e.g. * varTbl [idx] =get2ByteFloatVal()). the var table would be statically initialized (compiled).

Raphael303:
I'm not very experienced with this,

?

Maybe try changing that long if...then...else if... section to a switch?

       // Write commands
       switch (target) {
          case AWirkleistung: Wirkleistung = round(telegram->get4ByteFloatValue()); break;
          case AVerbrauch: Verbrauch = round(telegram->get4ByteFloatValue()); break;
          case AWechselrichterleistung: Wechselrichterleistung = round(telegram->get2ByteFloatValue()); break;
          case ASpeicherEnergie: SpeicherEnergie = round(telegram->get2ByteFloatValue()); break;
          ...
        }

But this may not help enough to get you under 100%. Your sketch is not that long. Its all the libraries that are used that make it so large in when compiled.

Raphael303:
It has to run on a pro Mini (or pro Micro).

Why? Size? There are other small Arduino with more flash memory.

gcjr:
in several cases, you have sequences of sub-function calls to the same sub-function with different argument. each sub-function call requires a bunch of code. arguments can be captures in tables and a single function used to call that sub-funciton (e.g. WriteEPDFloat()) with those arguments. All the existing sub-function calls are replaced with 2 function calls.

Thank you very much for taking time and looking at my sketch. I really appreciate it.
Could you elaborate on this. I see how this is a problem, but not what I would do about it.

PaulRB:
Maybe try changing that long if…then…else if… section to a switch?

       // Write commands

switch (target) {
         case AWirkleistung: Wirkleistung = round(telegram->get4ByteFloatValue()); break;
         case AVerbrauch: Verbrauch = round(telegram->get4ByteFloatValue()); break;
         case AWechselrichterleistung: Wechselrichterleistung = round(telegram->get2ByteFloatValue()); break;
         case ASpeicherEnergie: SpeicherEnergie = round(telegram->get2ByteFloatValue()); break;
         …
       }




But this may not help enough to get you under 100%. Your sketch is not that long. Its all the libraries that are used that make it so large in when compiled.
Why? Size? There are other small Arduino with more flash memory.

Also, thank you very much for taking time for this. I don’t take this for granted.

As for the switch. Never heard of this :slight_smile: Will check this out and learn what it is. It’s somewhat similar what gcjr wrote about the table of pointers?

Yes, size. There are other small ones with more flash memory… I just had a look. The zero? Never heard of that one… :o

Have a look at the Teensy range. More speed and more memory.

Raphael303:
Could you elaborate on this.

demonstrates use of tables (tested on laptop)

#include <stdio.h>

int SpeicherEnergie         = 10;
int Speicherleistung        = 11;
int Verbrauch               = 12;
int Wechselrichterleistung  = 13;
int Wirkleistung            = 14;

float TAussenSued             = 20;
float TAussenNord             = 21;
float TWohnzimmer             = 22;
float TSchlafzimmer           = 23;
float TSeminar                = 24;
float TAufenthaltsraum        = 25;
float TEstrich                = 26;
float TKesseltasche           = 27;

// -----------------------------------------------------------------------------
void
WriteEPDInt (
    int         id,
    const char* lbl,
    int         val,
    const char* units )
{
    printf ("    %s:   %2d %-20s %4d %s\n", __func__, id, lbl, val, units);
}

void
WriteEPDFloat (
    int         id,
    const char* lbl,
    float       val,
    const char* units )
{
    printf ("    %s: %2d %-20s %6.1f %s\n", __func__, id, lbl, val, units);
}

// -----------------------------------------------------------------------------
void
disp (void)
{
    printf ("\n%s:\n", __func__);
    WriteEPDInt(1, "Speicherenergie: ", SpeicherEnergie, " kWh");
    WriteEPDInt(2, "Speicherleistung: ", Speicherleistung, " W");
    WriteEPDInt(3, "Stromverbrauch: ", Verbrauch, " W");
    WriteEPDInt(4, "Photovoltaik: ", Wechselrichterleistung, " W");
    WriteEPDInt(5, "Strombilanz: ", Wirkleistung * (-1), " W");

    WriteEPDFloat(6, "Aussen Sued: ", TAussenSued, " C");
    WriteEPDFloat(7, "Aussen Nord: ", TAussenNord, " C");
    WriteEPDFloat(8, "Wohnzimmer: ", TWohnzimmer, " C");
    WriteEPDFloat(9, "Schlafzimmer: ", TSchlafzimmer, " C");
    WriteEPDFloat(10, "Seminarraum: ", TSeminar, " C");
    WriteEPDFloat(11, "Aufenthaltsraum: ", TAufenthaltsraum, " C");
    WriteEPDFloat(12, "Estrich: ", TEstrich, " C");
    WriteEPDFloat(13, "Kesseltasche: ", TKesseltasche, " C");
}

// -----------------------------------------------------------------------------
enum  {T_Int, T_Float };

typedef struct  {
    int         type;
    int         id;
    const char* lbl;
    void       *val;
    const char* units;
} Disp_t;

Disp_t dispTblData [] = {
    { T_Int,     1,   "Speicherenergie: ",  & SpeicherEnergie,   " kWh" },
    { T_Int,     2,   "Speicherleistung: ", & Speicherleistung,  " W" },
    { T_Int,     3,   "Stromverbrauch: ",   & Verbrauch,         " W" },
    { T_Int,     4,   "Photovoltaik: ",     & Wechselrichterleistung, " W" },
    { T_Int,     5,   "Strombilanz: ",      & Wirkleistung,      " W" },

    { T_Float,   6,   "Aussen Sued: ",      & TAussenSued,       " C" },
    { T_Float,   7,   "Aussen Nord: ",      & TAussenNord,       " C" },
    { T_Float,   8,   "Wohnzimmer: ",       & TWohnzimmer,       " C" },
    { T_Float,   9,   "Schlafzimmer: ",     & TSchlafzimmer,     " C" },
    { T_Float,   10,  "Seminarraum: ",      & TSeminar,          " C" },
    { T_Float,   11,  "Aufenthaltsraum: ",  & TAufenthaltsraum,  " C" },
    { T_Float,   12,  "Estrich: ",          & TEstrich,          " C" },
    { T_Float,   13,  "Kesseltasche: ",     & TKesseltasche,     " C" },
};

#define DispTblSize   (sizeof(dispTblData)/sizeof(Disp_t))

// ---------------------------------------------------------
void
dispTbl ()
{
    printf ("\n%s:\n", __func__);
    Disp_t  *p  = dispTblData;
    for (int n = 0; n < DispTblSize; n++, p++)  {
        if (T_Int == p->type)
            WriteEPDInt(p->id, p->lbl, *(int*)(p->val), p->units);

    else
            WriteEPDFloat(p->id, p->lbl, *(float*)(p->val), p->units);
    }
};

// -----------------------------------------------------------------------------
int
main (void)
{
    disp();
    dispTbl();
}
printf ("    %s: %2d %-20s %6.1f %s\n", __func__, id, lbl, val, units);
}

Oops

gcjr:
demonstrates use of tables (tested on laptop)

gcjr:
demonstrates use of tables (tested on laptop)

#include <stdio.h>

int SpeicherEnergie        = 10;
int Speicherleistung        = 11;
int Verbrauch              = 12;
int Wechselrichterleistung  = 13;
int Wirkleistung            = 14;

float TAussenSued            = 20;
float TAussenNord            = 21;
float TWohnzimmer            = 22;
float TSchlafzimmer          = 23;
float TSeminar                = 24;
float TAufenthaltsraum        = 25;
float TEstrich                = 26;
float TKesseltasche          = 27;

// -----------------------------------------------------------------------------
void
WriteEPDInt (
    int        id,
    const char* lbl,
    int        val,
    const char* units )
{
    printf ("    %s:  %2d %-20s %4d %s\n", func, id, lbl, val, units);
}

void
WriteEPDFloat (
    int        id,
    const char* lbl,
    float      val,
    const char* units )
{
    printf ("    %s: %2d %-20s %6.1f %s\n", func, id, lbl, val, units);
}

// -----------------------------------------------------------------------------
void
disp (void)
{
    printf ("\n%s:\n", func);
    WriteEPDInt(1, "Speicherenergie: “, SpeicherEnergie, " kWh”);
    WriteEPDInt(2, "Speicherleistung: “, Speicherleistung, " W”);
    WriteEPDInt(3, "Stromverbrauch: “, Verbrauch, " W”);
    WriteEPDInt(4, "Photovoltaik: “, Wechselrichterleistung, " W”);
    WriteEPDInt(5, "Strombilanz: “, Wirkleistung * (-1), " W”);

WriteEPDFloat(6, "Aussen Sued: “, TAussenSued, " C”);
    WriteEPDFloat(7, "Aussen Nord: “, TAussenNord, " C”);
    WriteEPDFloat(8, "Wohnzimmer: “, TWohnzimmer, " C”);
    WriteEPDFloat(9, "Schlafzimmer: “, TSchlafzimmer, " C”);
    WriteEPDFloat(10, "Seminarraum: “, TSeminar, " C”);
    WriteEPDFloat(11, "Aufenthaltsraum: “, TAufenthaltsraum, " C”);
    WriteEPDFloat(12, "Estrich: “, TEstrich, " C”);
    WriteEPDFloat(13, "Kesseltasche: “, TKesseltasche, " C”);
}

// -----------------------------------------------------------------------------
enum  {T_Int, T_Float };

typedef struct  {
    int        type;
    int        id;
    const char* lbl;
    void      val;
    const char
units;
} Disp_t;

Disp_t dispTblData = {
    { T_Int,    1,  "Speicherenergie: “,  & SpeicherEnergie,  " kWh” },
    { T_Int,    2,  "Speicherleistung: “, & Speicherleistung,  " W” },
    { T_Int,    3,  "Stromverbrauch: “,  & Verbrauch,        " W” },
    { T_Int,    4,  "Photovoltaik: “,    & Wechselrichterleistung, " W” },
    { T_Int,    5,  "Strombilanz: “,      & Wirkleistung,      " W” },

{ T_Float,  6,  "Aussen Sued: “,      & TAussenSued,      " C” },
    { T_Float,  7,  "Aussen Nord: “,      & TAussenNord,      " C” },
    { T_Float,  8,  "Wohnzimmer: “,      & TWohnzimmer,      " C” },
    { T_Float,  9,  "Schlafzimmer: “,    & TSchlafzimmer,    " C” },
    { T_Float,  10,  "Seminarraum: “,      & TSeminar,          " C” },
    { T_Float,  11,  "Aufenthaltsraum: “,  & TAufenthaltsraum,  " C” },
    { T_Float,  12,  "Estrich: “,          & TEstrich,          " C” },
    { T_Float,  13,  "Kesseltasche: “,    & TKesseltasche,    " C” },
};

#define DispTblSize  (sizeof(dispTblData)/sizeof(Disp_t))

// ---------------------------------------------------------
void
dispTbl ()
{
    printf ("\n%s:\n", func);
    Disp_t  *p  = dispTblData;
    for (int n = 0; n < DispTblSize; n++, p++)  {
        if (T_Int == p->type)
            WriteEPDInt(p->id, p->lbl, (int)(p->val), p->units);

else
            WriteEPDFloat(p->id, p->lbl, (float)(p->val), p->units);
    }
};

// -----------------------------------------------------------------------------
int
main (void)
{
    disp();
    dispTbl();
}

Holy shit! This is at the same time beautiful and amazing. It took me quite a while to get what you are doing there, but I totally see the beauty in it. I couldn’t have dreamed to come up with something like this myself in a 100 years. Just wow! And thank you!

this approach is much more common than you might think

"Algorithms + Data Structures = Programs" - Niklaus Wirth

Raphael303:
The zero?

You can't buy Zero from Arduino these days I think, but there are several designs of "clones" which are very cheap on eBay etc and the MKR and Nano 33 series from Arduino which are "descended" from the Zero.

There are also the esp8266 boards such as Wemos Mini and esp32 boards.

If you tell us more about your project and the components and modules you want to connect to the Arduino, we can be more certain about what boards we recommend.