Do libraries add memory overhead unnecessarily?

I'm working on some code for an ATTiny85 that uses the V-USB bootloader. As such I've only got about 5.3kb of usable flash memory.

I'm currently at 98% of that but really would like to add more functionality. I've run over my code a few times but not really sure where else I could optimize it. But then I started thinking about 2 libraries I'm using, the NeoPixel library, and also a OneButton library.

But I'm wondering, would I save program space by basically copying the library functionality directly into my sketch, rather than including them? Or is the contents of the .h file not actually included in the compiled code, just the C/CPP file itself?

Or, if anyone is curious, can you see something I can optimize in my code?

// avrdude -c usbasp -p attiny85 -b 19200 -U lfuse:w:0xF1:m -U hfuse:w:0xD5:m -U efuse:w:0xFE:m
// avrdude -c usbasp -p attiny85 -b 19200 -U flash:w:flash_me_lv.hex:i
// Use slow clock!

#include <Adafruit_NeoPixel.h>
#include <avr/power.h> // Include for 16Mhz mode
#include <OneButton.h>
#include <EEPROM.h>

#define PIN 0 // LED Pin

Adafruit_NeoPixel strip = Adafruit_NeoPixel(50, PIN, NEO_GRB + NEO_KHZ800);
OneButton button(1, false); // Button on pin 1, active high

uint16_t k, j = 0;
long previousMillis = 0;
long prevMillis = 0;
int voltage;
boolean iterated = false;
byte colwipe = 1;
boolean isFlat = false;
byte wheelcol = 0;
int bright = EEPROM.read(0);
byte dispmode = EEPROM.read(1);
byte wheelcolv[4] = {EEPROM.read(2), EEPROM.read(3), EEPROM.read(4), EEPROM.read(5)};

void setup() {
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1); // Run at 16Mhz
  strip.begin();
  strip.show(); // Initialize pixels
  strip.setBrightness(bright);
  button.attachClick(changeMode);
  button.attachDuringLongPress(changeVars);
  button.attachLongPressStop(saveVars);
  button.attachDoubleClick(turnOff);
 // Stuff to write initial EEPROM config
  /*EEPROM.write(0, 32); 
  EEPROM.write(1, 1); // dispmode
  EEPROM.write(2, 20); // First colour
  EEPROM.write(3, 60); // 2nd colour
  EEPROM.write(4, 127); // 3rd colour
  EEPROM.write(5, 160); // 4th colour
  */
}

void loop() {
  button.tick(); // Check button
  if (map(analogRead(1),0,1023,0,5000) < 3300 ){ // If voltage is under 3.3V-ish
    isFlat = true;
  }
  if (isFlat == false){
    if (dispmode < 4){ // User-define colours
      for (int i=0; i<strip.numPixels(); i++){
         strip.setPixelColor(i, Wheel(wheelcolv[dispmode]));
       }
       strip.show();
    } else if (dispmode == 4){ // Colour wipe mode
      if (colwipe == 1) {
        colorWipe(strip.Color(255, 0, 0), 20); // Red
      }
      if (colwipe == 2) {
        colorWipe(strip.Color(0, 255, 0), 20); // Green
      }
      if (colwipe == 3) {
        colorWipe(strip.Color(0, 0, 255), 20); // Blue
      }
    } else if (dispmode == 5){ // Rainbow scroll mode
      rainbowCycle(1);
    } else if (dispmode == 6){ // Slow fade mode
      fade(1);
    }
  } else { // If the battery is flat
    sclear();
    for (int i=0; i<5; i++){
      strip.setPixelColor(0,255,0,0); // Flash the first LED red
      strip.show();
      delay(1000);
      strip.setPixelColor(0,0,0,0);
      strip.show();
      delay(1000);
    }
    digitalWrite(4, LOW);
  }
}

void changeMode() { // Change the program
  if (dispmode < 6){
    dispmode++;
    j = 0;
    k = 0;
    //Serial.println(dispmode);
  } else {
    dispmode = 0;
  }
  EEPROM.write(1, dispmode); // Save current program to EEPROM for next start
}

void changeVars(){
  if (dispmode < 4){ // If in user-define mode, long press changes the colour
    if (millis() - prevMillis > 10){
      prevMillis = millis();
      if (wheelcolv[dispmode] < 255){
        wheelcolv[dispmode]++;
      } else {
        wheelcolv[dispmode] = 0;
      }
    }
    //delay(20);
  } else { // In the other modes, long press changes brightness
    if (millis() - prevMillis > 40){
      prevMillis = millis();
      strip.setBrightness(bright);
      bright++;
      if (bright > 80 ){
        bright = 8;
      }
    //delay(15);
    }
  }
}

void saveVars(){ // Save config stuff to EEPROM
    EEPROM.write(0, bright);
    EEPROM.write(2, wheelcolv[0]);
    EEPROM.write(3, wheelcolv[1]);
    EEPROM.write(4, wheelcolv[2]);
    EEPROM.write(5, wheelcolv[3]);
}

void turnOff(){ // Turn the boost converter off
  digitalWrite(4, LOW);
}

void sclear() { // Set strip to off
  for (int i = 0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i,0,0,0);
  }
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint16_t wait) {
  if(millis() - previousMillis > 40) {
    previousMillis = millis();  
    if (k < strip.numPixels()){
        strip.setPixelColor(k, c);
        strip.show();
        //delay(wait);
        k++;
    } else {
      k = 0;
      colwipe++;
     if (colwipe > 3){
        colwipe = 1;
      }
    }
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  if (j < 256) { 
    if (iterated) {
      j += 4;
      iterated = false;
    }
    if (k < strip.numPixels()) {
      strip.setPixelColor(k, Wheel(((k * 256 / strip.numPixels()) + j)));
      k++;
    } else {
      k = 0;
      iterated = true;
    }
    strip.show();
    if (iterated == true){
      delay(wait);
      //iterated = false;
    }
  } else {
    j = 0;
  }
}

void fade(uint8_t wait) { // Whole-strip fade
  if (j < 255) {
    if (k < strip.numPixels()){
      strip.setPixelColor(k,Wheel(j));
      k++;
    } else {
      k = 0;
      j += 4;
    }
    strip.show();
    delay(wait);
  } else {
    j = 0;
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

Cheers

Of course that the library add code to your program. The .h file don't have the code of the library, it's only the "presentation" of the functions that the library implements. The code itself is in the .cpp file.

OK, so the .h file is only used by the compiler itself? That's basically what I was asking, but I could have worded it better.

Yes, basically, is only used by the compiler.

Sorry for hijacking this thread but an interesting question would be... If I add a library but don't use/call all the functions in that library then is the entire library code added of just the used sections of code (functions) within the library. I know that the library may call some of it's own functions but what about ones that are not used by either my code or the functions I call in the library.

The compiler only includes the functions you actually use. I recently did a comparison where I was using only one function from the standard C <strings.h> library. It was one if the simpler functions, so I wrote my own version, directly in my code, not even calling it as a function.

My version was 1 byte smaller than the library version. So #include <strings.h> only cost me one byte in program size.

The thing to watch out for is some library functions can be large. If you have one single floating-point division in your code then it has to include a rather large function. If you can re-write to remove that single floating-point operation, then the compile doesn't include that code and your compiled program shrinks dramatically.

Yeah I was curious about that too originally, though I think it says somewhere in the reference on libraries that it only includes parts of the library it needs - which kind of makes the suggestions of going through libraries and removing unused code kind of redundant.

On topic though, I've gone and reflashed the ATTiny85 with the micronucleus bootloader, so I've just given myself an extra 1.2kb of space in doing that. Downside is I'm not sure if anyone has found a way to make it work directly with the IDE, so I have to upload the hex manually with it's own uploader.

It should be made clear that there is a difference between using the String class (note uppercase 'S') and the standard C string processing functions. Objects from the String class do bring in a fairly large amount of code; usually more that you might actually use. Other functions do the same. For example, using sscanf() to format from char data to int often carries a 30% bloat factor in code size. The reason is because sscanf() can do a lot of other things that your code doesn't use. Most prefer to use the string processing functions (e.g., strtok()) and then use atoi() for make the conversion.

It depends on the library. Sometimes there's a difference. But if it's coded well then, no, you don't really save anything significant. The library may do more than you really need in which case you could simplify it.

I'm curious about the bootloader though. I don't know anything about bootloaders, but I've noticed that the one for my Uno is only 512 bytes. Why is yours so large? What advantage does it provide?

You could use no bootloader and then have the entire 8K available.