Reducing Sketch by 70 bytes...

Hi there,

I run into compiling problem, I wish to use ATmega48V-10PU (4K Flash) with my sketch, I got this message:

Sketch uses 4162 bytes (101%) of program storage space. Maximum is 4096 bytes.
Global variables use 366 bytes (71%) of dynamic memory, leaving 146 bytes for local variables. Maximum is 512 bytes.
Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.
Error compiling for board ATmega48 (no bootloader).

So, I already reduced my current code by 2% (before it was 103%), but I stuck, need to be reduced by 70 bytes more.

My Sketch:

#include "Wire.h"
#include "Keypad.h"

#define I2C_ADDR    0x1A

char pKey;

const byte nRows = 4;
const byte nCols = 4;

char kMap[nRows][nCols]= 
{
{'1', '2', '3', 'A'}, 
{'4', '5', '6', 'B'}, 
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};

byte rPins[nRows] = {9,8,7,6};
byte cPins[nCols]= {5,4,3,2};

Keypad myKeyPad = Keypad(makeKeymap(kMap), rPins, cPins, nRows, nCols);

void setup()
{
  Wire.begin(I2C_ADDR);
  Wire.onRequest(requestEvent);
}

void loop()
{
  pKey = myKeyPad.getKey();
  delay(100);
}

void requestEvent()
{
  if (pKey != NO_KEY)
  {
    pKey = pKey;
  } else {
    pKey = 'X';  
  }
  Wire.write(pKey);
}

Is there any chance to reduce maybe the library?

Libs used (Keypad.h):

#ifndef KEYPAD_H
#define KEYPAD_H

#include "utility/Key.h"

// Arduino versioning.
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#ifndef INPUT_PULLUP
#warning "Using  pinMode() INPUT_PULLUP AVR emulation"
#define INPUT_PULLUP 0x2
#define pinMode(_pin, _mode) _mypinMode(_pin, _mode)
#define _mypinMode(_pin, _mode)  \
do { \
 if(_mode == INPUT_PULLUP) \
 pinMode(_pin, INPUT); \
 digitalWrite(_pin, 1); \
 if(_mode != INPUT_PULLUP) \
 pinMode(_pin, _mode); \
}while(0)
#endif


#define OPEN LOW
#define CLOSED HIGH

typedef char KeypadEvent;
typedef unsigned int uint;
typedef unsigned long ulong;

typedef struct {
    byte rows;
    byte columns;
} KeypadSize;

#define LIST_MAX 10 // Max number of keys on the active list.
#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns)
#define makeKeymap(x) ((char*)x)


//class Keypad : public Key, public HAL_obj {
class Keypad : public Key {
public:

 Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols);

 virtual void pin_mode(byte pinNum, byte mode) { pinMode(pinNum, mode); }
 virtual void pin_write(byte pinNum, boolean level) { digitalWrite(pinNum, level); }
 virtual int  pin_read(byte pinNum) { return digitalRead(pinNum); }

 uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns.
 Key key[LIST_MAX];
 unsigned long holdTimer;

 char getKey();
 bool getKeys();
 KeyState getState();
 void begin(char *userKeymap);
 bool isPressed(char keyChar);
 void setDebounceTime(uint);
 void setHoldTime(uint);
 void addEventListener(void (*listener)(char));
 int findInList(char keyChar);
 int findInList(int keyCode);
 char waitForKey();
 bool keyStateChanged();
 byte numKeys();

private:
 unsigned long startTime;
 char *keymap;
    byte *rowPins;
    byte *columnPins;
 KeypadSize sizeKpd;
 uint debounceTime;
 uint holdTime;
 bool single_key;

 void scanKeys();
 bool updateList();
 void nextKeyState(byte n, boolean button);
 void transitionTo(byte n, KeyState nextState);
 void (*keypadEventListener)(char);
};

#endif

and (Key.h):

#ifndef KEY_H
#define KEY_H

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define OPEN LOW
#define CLOSED HIGH

typedef unsigned int uint;
typedef enum{ IDLE, PRESSED, HOLD, RELEASED } KeyState;

const char NO_KEY = '\0';

class Key {
public:
 // members
 char kchar;
 int kcode;
 KeyState kstate;
 boolean stateChanged;

 Key();
 Key(char userKeyChar);
 void key_update(char userKeyChar, KeyState userState, boolean userStatus);

private:

};

#endif

Thank you for your effort! :wink:

  pKey = myKeyPad.getKey();
  delay(100);

This is useless code. You don't, in loop(), care what value is read from the keypad, so why bother reading?

Why do you care if loop() iterates 10 times per second or 100 or 1000 or 1000000? You don't save any resources using delay(), so get rid of it.

void requestEvent()
{
  if (pKey != NO_KEY)
  {
    pKey = pKey;
  } else {
    pKey = 'X';  
  }
  Wire.write(pKey);
}

Assigning the same value to pKey again, mmm. Probably optimized away but x=x is never useful :stuck_out_tongue:

void requestEvent()
{
  if (pKey != NO_KEY)
  {
    Wire.write(pKey);
  } 
  else {
    Wire.write('X');
  }  
}

Select Tools > Compiler LTO > Enabled then compile and check to see if that helped. If you encounter an compilation error after doing this it means you need to update your IDE to a new version. If it still fails then you also need to use Boards Manager to update to the latest version of Arduino AVR Boards.

Add the following line to your sketch in the global scope:

void serialEventRun(){}

That should save some bytes of program memory and even makes loop() run slightly faster. The only downside is you lose the use of the idiotic serialEvent() thing but that's no loss, especially since you're not using it.

septillion:
x=x is never useful :stuck_out_tongue:

It is useful to suppress the "unused parameter warning" of a function sceleton without generating code.

void someFunc(int x) {
  x = x;
}

Whandall:
It is useful to suppress the "unused parameter warning" of a function sceleton without generating code.

void someFunc(int x) {

x = x;
}

If you don't use it (like a dummy argument for overloading/overriding), you don't have to give it a name.

void someFunc(int) {}

Will that suppress the warning?

Tested it myself and it suppresses the warning too.

I've used:

(void)x;

to fix the unused parameter warning in a couple different cases, I think where preprocessor conditionals would cause the variable to not be used in some cases. It seems maybe a bit more elegant that way but as long as a comment is added to explain the purpose of this strange line and the compiler optimizes it away I suppose either is just as good.

I think I will go for

void withUnusedParameter(int /*x*/) {
}

in the future, which preserves the name that could carry some information,
but that would not work with compile time conditionals.

Maybe the last resort for x = x; is in an added #else section....

pert:
Select Tools > Compiler LTO > Enabled then compile and check to see if that helped. If you encounter an compilation error after doing this it means you need to update your IDE to a new version. If it still fails then you also need to use Boards Manager to update to the latest version of Arduino AVR Boards.

Add the following line to your sketch in the global scope:

void serialEventRun(){}

That should save some bytes of program memory and even makes loop() run slightly faster. The only downside is you lose the use of the idiotic serialEvent() thing but that's no loss, especially since you're not using it.

Your proposition did the trick very well, better than I expected, the whole Sketch can fit in now, not just my reduced what I posted above! 8)

Sketch uses 3426 bytes (83%) of program storage space. Maximum is 4096 bytes.
Global variables use 335 bytes (65%) of dynamic memory, leaving 177 bytes for local variables. Maximum is 512 bytes.

Thank you to all for helping me out, especially kudos for @pert! :wink:

UPDATE:

"Select Tools > Compiler LTO > Enabled then compile and check to see if that helped. "

Whit this settings will compile just fine, without any errors, but the Sketch are not working.

The I2C scanner (Arduino Uno) will find the Slave device (ATmega48V-10PU) on 0x1A address, but wont send any data from Keypad.

If I do the same thing with ATmega8-16PU (without enabling Compiler LTO, because the Sketch fits into the chip), everything works just fine.

Any other propositions, suggestions on Sketch reducing?

UPDATE 2:

After removing everything from Loop() and changed the requestEvent() procedure code to:

void requestEvent()
{
  pKey = myKeyPad.getKey();
  if (pKey != NO_KEY)
  {
    Wire.write(pKey);
  } else {
    Wire.write('X');
  }  
}

I got it reduced by:

Sketch uses 4020 bytes (98%) of program storage space. Maximum is 4096 bytes.
Global variables use 368 bytes (71%) of dynamic memory, leaving 144 bytes for local variables. Maximum is 512 bytes.

It's working fine now with the ATmega48V-10PU, but can it be reduced more?

Why did not work with Compiler LTO > Enabled option?

Any thoughts?

That's unfortunate about your problem with LTO. LTO stands for link time optimization and it's a pretty aggressive compiler optimization setting so it is possible for it to break things but this setting is turned on in all versions of Arduino AVR Boards since the one included with Arduino IDE 1.6.10. This means that for the last 11 months many thousands of people have been using that setting with their Unos, Megas, Leonardos, etc. with almost no problems reported. The reason it's not enabled by default in MiniCore is that the setting requires the latest version of avr-gcc to be installed and some people are still using old versions of the Arduino IDE/Arduino AVR Boards which don't support LTO. So it hasn't been used by many people with the ATmega48. It might require some investigation to determine what's being broken by the optimization.

If you set File > Preferences > Compiler warnings > All and then compile with LTO enabled do you see any warnings displayed in the log window at the bottom of the Arduino IDE window?

pert:
That's unfortunate about your problem with LTO. LTO stands for link time optimization and it's a pretty aggressive compiler optimization setting so it is possible for it to break things but this setting is turned on in all versions of Arduino AVR Boards since the one included with Arduino IDE 1.6.10. This means that for the last 11 months many thousands of people have been using that setting with their Unos, Megas, Leonardos, etc. with almost no problems reported. The reason it's not enabled by default in MiniCore is that the setting requires the latest version of avr-gcc to be installed and some people are still using old versions of the Arduino IDE/Arduino AVR Boards which don't support LTO. So it hasn't been used by many people with the ATmega48. It might require some investigation to determine what's being broken by the optimization.

If you set File > Preferences > Compiler warnings > All and then compile with LTO enabled do you see any warnings displayed in the log window at the bottom of the Arduino IDE window?

I'm using Arduino IDE version 1.8.2 and avr-gcc (4.9.2-atmel3.5.4-arduino2)

Enabled all warnings as you suggested, but it's just compiles fine without any additional message:

Sketch uses 3332 bytes (81%) of program storage space. Maximum is 4096 bytes.
Global variables use 335 bytes (65%) of dynamic memory, leaving 177 bytes for local variables. Maximum is 512 bytes.

Regards

If you want to see more messages, select File:Preferences and turn on Verbose output.

CrossRoads:
If you want to see more messages, select File:Preferences and turn on Verbose output.

Yes, I did that too, all nice white text/message output...no orange colored warnings there.

Regards

Yes, I did that too, all nice white text/message output...no orange colored warnings there.

The coloring in the output window is about as consistent, and useful, as is the coloring in the editor window.