While developing ArduinoJson, I’ve always been obsessed with code size. Arduinos have such a small amount of Flash (32KB for a Duemilanove) that every byte is important.

Here are two techniques that I use. I tested them on Windows for the AVR platform, they can probably be adapted to other platforms.

We’ll use two tools from the AVR tool chain: avr-nm and avr-objdump , they are both provided along with the Arduino IDE.

Finding the .elf file

So let’s assume that you wrote an Arduino sketch, and you want to optimize the size.

Once you compiled it, the first thing you need to do is find the compiled binary file. The easiest way is to “Enable verbose output during compilation” and look for the path to the .elf file at the end of the log.

View code size with avr-nm

When optimizing the size of a program, it’s very important to focus your effort on the right place. Using avr-nm , you’ll know the size of each function.

Open a command prompt and type:

set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\ avr-nm --size-sort -C -r "<path-to-the-elf-file>"

The size of all functions will be printed:

00000094 T __vector_16 00000076 T init 00000072 T pinMode 0000006c T digitalWrite 00000052 t turnOffPWM 00000050 T delay 00000046 T micros 00000028 T loop 0000001e T main 00000014 T digital_pin_to_timer_PGM 00000014 T digital_pin_to_port_PGM 00000014 T digital_pin_to_bit_mask_PGM 00000010 T __do_clear_bss 0000000a T port_to_output_PGM 0000000a T port_to_mode_PGM 00000008 T setup 00000004 B timer0_overflow_count 00000004 B timer0_millis 00000002 W yield 00000002 W initVariant 00000002 t __empty 00000001 b timer0_fract

Functions are printed with the biggest on top of the list and the size is shown in hexadecimal.

View assembly with avr-objdump

When avr-nm is not enough, you have no choice but use the biggest weapon: the disassembler. With avr-objdump you can extract the assembly code from the .elf file.

In a command prompt, type:

set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\ avr-objdump -C -d "<path-to-the-elf-file>"

The assembly code of the whole program will be printed. Here it what it shows for the loop() function of the Blink example:

000000e8 <loop>: e8: 61 e0 ldi r22, 0x01 ; 1 ea: 8d e0 ldi r24, 0x0D ; 13 ec: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite> f0: 68 ee ldi r22, 0xE8 ; 232 f2: 73 e0 ldi r23, 0x03 ; 3 f4: 80 e0 ldi r24, 0x00 ; 0 f6: 90 e0 ldi r25, 0x00 ; 0 f8: 0e 94 f5 00 call 0x1ea ; 0x1ea <delay> fc: 60 e0 ldi r22, 0x00 ; 0 fe: 8d e0 ldi r24, 0x0D ; 13 100: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite> 104: 68 ee ldi r22, 0xE8 ; 232 106: 73 e0 ldi r23, 0x03 ; 3 108: 80 e0 ldi r24, 0x00 ; 0 10a: 90 e0 ldi r25, 0x00 ; 0 10c: 0c 94 f5 00 jmp 0x1ea ; 0x1ea <delay>

We can see the calls to digitalWrite() and delay() from the Blink example.