Strategies to shrink the size of my sketch

Hello all, So I've been messing around with Arduino for a few years now and I just finished Jeremy Blums Arduino book. I'm attempting to make a fish tank data display/dosing pump controller. I started programming this on a Mega and I've just transferred it over to a Pro mini. unfortunately, I'm already at around 85% dynamic memory usage on the mini and I haven't even added the RTC code, pump calculations, etc. I feel like this should be a fairly small program but I'm just inexperienced so it's not :sweat_smile: . Anyways if someone would be kind enough to take a look at my sketch and give me some pointers I'd appreciate that very much. Thank you!!

Aquarium_Controller_V1.zip (4.2 KB)

Aquarium_Controller_V1.ino (9.7 KB)

Dosing_Pump_Settings_Screen.ino (5.7 KB)

Monitoring_Screen.ino (999 Bytes)

Settings_Main_Screen.ino (970 Bytes)

Zipping it was a good strategy to shrink the code size here :)) ... but Post code directly using code tags (and indent the code) - so we can read this on a mobile

I just tried, unfortunately, my sketch is well over 9000 characters and to get it below the limit I have to omit most of the code. (may be why it takes up so much memory? :sweat_smile: )

harry88:
I just tried, unfortunately, my sketch is well over 9000 characters...

Attaching the four individual files is a somewhat reasonable compromise.

Ok, can do!
They're attached.

Aquarium_Controller_V1.ino (9.7 KB)

Dosing_Pump_Settings_Screen.ino (5.7 KB)

Monitoring_Screen.ino (999 Bytes)

Settings_Main_Screen.ino (970 Bytes)

MorganS:
https://www.arduino.cc/en/Tutorial/Memory

So I read that and I believe I've done at least a halfway decent job of using the smallest data type possible where I can. I think my biggest issue is my strings taking up too much memory and I've used the print function with the F flag where possible. Or are you suggesting I use an external storage device to store data? I know my RTC said I could write to its RAM.

In Settings_Main_screen, you have:

[nobbc]u8g2.print(String (CFchar[CF]));[/nobbc]

As a rule, do not use the C++ String class, ever. To print a C or F depending on the value of CF, the u8g2 should have a print function that takes a single char value

[nobbc]u8g2.print(CFchar[CF]);[/nobbc]

If it doesnt, then you probably want to build a C-string:

char ch[2];
ch[0] = CFchar[CF];
ch[1] = '\0';
u8g2.print(ch);

This allocates storage on the stack rather than on the heap.

Apart from that … I can't really see anything that would take up a lot of space. You may find that even though memory use is 85%, most of that is the libraries you are including. This means that the rest of the code you want to add probably won't take a lot of additional space.

There's several style changes I would make, of course.

class Pump {
  public:

  byte Pump1DoseVal; // How much ML should pump dose//
  byte HourVal = 1 ; //Hour Val of when pump should dose//
  byte MinVal = 1; //min Val of when pump  should dose//
  boolean AmPm = false; // AM or PM of when pump  should dose//
  boolean onoff = false; //on off state for pump //
};

Pump pump1;
Pump pump2;

I'd turn those screens of yours into a data-driven structure, and label all those magic numbers. For that matter, punp1 and pump2 would be named 'foodPump' and 'medicinePump', or whatever they are. But this doesn't really matter much at this point.

TL;DR: don't worry about it. The rest of the code you want to write wont take up an additional 15%. But do get rid of any reference to the String class.

Ok thank you very much! Could you elaborate on what you mean by "data driven structure"? But seriously thank you that helps a lot!

MAP files are rather handy for determining how memory is being used...

http://forum.arduino.cc/index.php?topic=72948.0

How many font do you use?

J-M-L:
How many font do you use?

That's a good point. That's probably what's taking the lion's share of the memory.

It might be possible to configure things so that the fonty stuff only loads up the glyphs that you actually use … perhaps you might need to edit the library itself, to create a smaller library that doesn't know how to draw an uppercase 'O' (because your sketch doesn't need it).

But it's not necessary. You still have 15% of memory left, that will be enough for what you need, so what if there's a bunch of font data that never gets used? But if you do run low on memory, then yes just using one font and using a version of the library that only includes that font is totally an option. If the library supports this, it probably does so using some #define directives. It would look something along the lines of:

#define SCREENLIB_DONT_NEED_ITALIC_FONT 1
#define SCREENLIB_DONT_NEED_BOLD_FONT 1
#include <screenlib.h>

The documentation for the library should specify how to do this. Or you may need to ferret through the library source code.

Could you elaborate on what you mean by "data driven structure"?

Making a data-driven structure is … probably more complex than you might want to fool with at this point. It's more the sort of thing you'd do if you had a deep tree of submenus.

With an appropriate set of constructors or builder methods, you'd build up the menus and possibly the screens using sets of declarations

A 'menu' would be a data structure containing an array of menu items. Each item would be either a 'submenu' line or a 'actual input screen' item. Submenu items would have a method allowing the controller code to ask "if someone clicks you, what submenu/page do I display?". The controller code tracks which submenu or page it is currently displaying, and where to return to whne the 'back' button is pressed.

Likewise, a 'page' object might have an array of fields, each specifying a row/column where it is to appear, it's width, a type, and a current value. The controller code would handle the job of talking to your input devices.

But honestly - it's really more than you need.

One thing, however: your code currently redraws each page completely every time it loops. Really, the static parts of the pages only really need to be redrawn when the page changes. This is maybe the main advantage of the data driven approach. This stuff:

  if (InHorizontalMenu == 1 && PageVal == 6 && ColumVal == 1) {
    if (AxisYv < 25) {
      P1HourVal++;
      delay(250); //delay between reads so it doesn't change through lines all at once//
    }

    if (AxisYv > 1005) {
      P1HourVal --;
      delay(250); //delay between reads so it doesn't change through lines all at once//
    }
    P1HourVal = constrain(P1HourVal, 1, 12);
  }

becomes more like

  Field *f = findFieldFor(PageVal, ColumnVal);
  if(f != NULL && f->talksToYaxis()) {
    int v = f->value;
    if (AxisYv < 25) {
      v++;
    }

    if (AxisYv > 1005) {
      v --;
    }

    v = constrain(v, f->min, f->max);
    if(f->value != v) {
      f->value = v;
      redrawField(f);
      delay(250);
    }
  }

I'm rambling. It's 0:20 AM - I'll stop now.

Thank you very much everyone. This has helped me very much an I have a lot of things I need to look into.