I am using an ATTiny85 for the first time and at compile I receive the following errors

Arduino: 1.6.11 (Windows 10), Board: "Digispark (Default - 16.5mhz)"

C:\Users\Steve\AppData\Local\Temp\build129e80c0e73568918bca55047472d138.tmp/Smoke_Auto-ATtiny.ino.ino.elf section `.text' will not fit in region `text'

c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: region `text' overflowed by 562 bytes

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Digispark (Default - 16.5mhz).

The chip is

Atmel 1344

I think what is happening is that my code is too big for the board. Am I right?

Assuming so, ignoring any obvious programming errors (I can't debug for this board until I can upload :P ) how can I squeeze code like this onto an ATTiny? I am not looking for someone to rewrite it for me, I am more interested in learning where there is any obvious bloat - I suspect the IR library (IRremote Version 2.0.1 June, 2015 by Ken Shirriff : 11Kb)

FWIW this is a project to convert a manually operated smoke machine into an automatic one and be able to manipulate the smoke time and delay via a remote. It works on a Mega, proof of concept complete, I want to shrink the board to fit into the mouse-sized controller if that is possible.

#include <IRremote.h>

long smokeTimeInit = 5000;  // initial smoke time
long smokeWaitInit = 60000;  // initial interval between smoke

long smokeIncrement = 1000; // timer increment value
long delayIncrement = 5000; // timer increment value

unsigned long previousMillis = 0; // this is our stored time
unsigned long currentMillis;  //where we keep count of time

const int smokeOnPin = 2;  // output to relay
int smokeStopped = false; // flag to stop smoke
const int LED = 0;
const long RECV_PIN = 1;  // input from IR receiver

IRrecv irrecv(RECV_PIN); 

decode_results results;

long  smokeTime = smokeTimeInit;
long  smokeWait = smokeWaitInit;  

void setup() {
  pinMode(smokeOnPin, OUTPUT);
  pinMode(LED, OUTPUT); 

  irrecv.enableIRIn(); // Start the receiver 

  digitalWrite(smokeOnPin, LOW);  // start with the smoke on
  digitalWrite(LED, HIGH);


void loop() {
  if (irrecv.decode(&results)) {

      case 284157975 :
      //more smoke time
        smokeTime = smokeTime + smokeIncrement;
        smokeStopped = false; // smoke on

      case 284104935 :
      //less smoke time
        if(smokeTime > smokeIncrement){ // don't let the smoke time become less than the increment value i.e. negative
          smokeTime = smokeTime - smokeIncrement;
          smokeTime = 1000;
        smokeStopped = false;

      case 284125335 :
      //reduce interval
        if(smokeWait > delayIncrement){ 
          smokeWait = smokeWait - delayIncrement;
          smokeWait = 1000;
        smokeStopped = false;  // smoke on

      case 284141655 :
      // increase interval
        smokeWait = smokeWait + delayIncrement;
        smokeStopped = false;  // smoke on

      case 284140125 :
      // reset to initial time values
        smokeTime = smokeTimeInit;
        smokeWait = smokeWaitInit;
        smokeStopped = false;  // smoke on

      case 284148285 :
      // reset but with shorter smoke time
        smokeTime = 1000;
        smokeWait = 60000;
        smokeStopped = false;  // smoke on

      case 284123805 :
      // reset with short smoke and shorter interval
        smokeTime = 2000;
        smokeWait = 30000;
        smokeStopped = false;  // smoke on

      case 284131965 :
      // stop
        smokeStopped = true;


currentMillis = millis();  

if(smokeStopped){  // don't even do this if smoke stopped
    if (currentMillis - previousMillis >= smokeTime && digitalRead(smokeOnPin)==0) {
      digitalWrite(smokeOnPin, HIGH); // turn the smoke on
      digitalWrite(LED, LOW);
      previousMillis = currentMillis;
    else if (currentMillis - previousMillis >= smokeWait && digitalRead(smokeOnPin)==1 ) {
      digitalWrite(smokeOnPin, LOW); // turn the smoke off
      digitalWrite(LED, HIGH);
      previousMillis = currentMillis;

  • Please edit your question to include which ATtiny -- memory sizes vary a lot, eg ATtiny13 at 1K Flash, vs 4K and 8K in ATtiny48 and 88. Also say which IRremote.h library you have -- some on my system are 2670 bytes of source, vs 2844 and 4381 in other versions. Jan 13, 2017 at 1:57
  • @jwpat7 thanks for that. I have added that it is an ATTiny 85 and some info about the IRRemote library.
    – Steve
    Jan 13, 2017 at 2:10
  • If dumping the Arduino libraries isn't viable then you could try using a 16kB tinyAVR instead. Jan 13, 2017 at 2:20
  • I just compiled it for a Leonardo and this: Sketch uses 10,562 bytes (36%) of program storage space. Maximum is 28,672 bytes. Global variables use 401 bytes (15%) of dynamic memory, leaving 2,159 bytes for local variables. Maximum is 2,560 bytes.
    – Steve
    Jan 13, 2017 at 2:28

2 Answers 2


Yes, the message “... Smoke_Auto-ATtiny.ino.ino.elf section .text' will not fit in regiontext'” suggests the code is too big to fit in the 8K available on the ATtiny85.

[In a .elf file, the TEXT section contains executable code.]

In IRremote.h from Ken Shirriff et al, some comments at line 28 say to disable protocols you don't need:

// Supported IR protocols
// Each protocol you include costs memory and, during decode, costs time
// Disable (set to 0) all the protocols you do not need/want!

By default, about 13 protocols are enabled and three disabled. If you only use one or two kinds of remotes, you can disable a bunch of the protocols.

Here are a couple of minor things for reducing memory, etc.:

• Instead of switching on results.value, a long, you could compute an unsigned int equal to results.value-284100000, since all of your case values start with 2841 and the largest is 284157975.

• Instead of having smokeStopped = false; // smoke on seven times within various cases, and smokeStopped = true; in one of them, set smokeStopped to false before the switch, and drop the seven settings-to-false from within the switch.

• Some of your time-related variables would be better as unsigned long instead of just long variables.

Generally, your code looks clean enough except for those items. Also, the IRremote code doesn't look bloated; but its tables can take a lot of memory.

Edit 1– Addressing crowie's comment about using #define instead of const int, one can use #define's, but in general the modern thought is that const int is preferable for type safety. Typically there is no difference in generated code – aside from type checking, the compiler treats a const int the same way as a #define, ie does not allocate memory for it if it isn't used, or if the value is small, may use a load-immediate instead of fetching a value from memory.

My own preference is to use enum { ... } to declare integer constants. For example, instead of

const int smokeOnPin = 2;  // output to relay
const long RECV_PIN = 1;  // input from IR receiver

I'd write

enum { smokeOnPin=2, IRinPin=1 }; // Pin# for smoke on and IR input
  • This is great, learned a lot before I even try to implement it. With the smokeStopped flags, my thinking was that if the smoke was stopped and I then changed one of the values with the remote, the smoke would start. But perhaps that is a luxury I can't afford and I will have to have a 'start' button.
    – Steve
    Jan 13, 2017 at 2:49
  • Wow...each of the IR protocols uses 500 bytes, chopping the ones I don't need reduced the size dramatically.
    – Steve
    Jan 13, 2017 at 3:21
  • and another thing if you have a constant dont use const variables instead use #define and put the variable nmae in all caps . This a convention of c programming ie #define SMOKEONPIN 2
    – crowie
    Jan 13, 2017 at 3:27
  • @crowie, see edit 1 Jan 13, 2017 at 3:45
  • @jwpat7 yes thats a good point I tend to use define because I like sticking to pure C. Arduino is different because it blurs the line between C++ and C and they are different languages even though they look the same. But with #define you can still add the type tag onto the end ie. 100U or 100UL or 100.0f, if you want to be specific about type
    – crowie
    Jan 13, 2017 at 4:18

The avr-nm tool is great for figuring out what is using up all your memory.

You need to get to a command-line in the temporary directory where your program is compiled. You can find this directory by looking at the scrolling bottom window when you compile your program.

You run the tool like this...

C:\Users\passp\AppData\Local\Temp\arduino_build_965235>"C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-nm" -S --size-sort blink.ino.elf

...where in my case...

C:\Users\passp\AppData\Local\Temp\arduino_build_965235 is the temporary directory that the Arduino was using

C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\ and the directory where the Arduino toolchain was installed

blink.ino.elf is the output generated from compiling my blink.ino sketch.

The -S parameter tells the tool to output the sizes of the symbols listed. By looking at these sizes, we can figure out what is using up all the memory and where to focus our reduction efforts.

The --size-sort shorts the symbols in size order so we can easily see which are the biggest.

The output of the tool on my program looks like...

0080010b 00000001 b click 0080008e 00000001 B datLen 00800088 00000001 b IRcount.1978 00800089 00000001 B msgNum 00800086 00000001 b prevVals.1995 0080008f 00000001 B progDir 00800087 00000001 b sendState.1979 00800109 00000001 b state 0080010a 00000001 b sync 00800092 00000001 B wake 00800066 00000002 D buttonCB 00800068 00000002 D clickCB 0080010c 00000002 B comBuf 00800115 00000002 B datBuf 0000006a 00000002 T emptyCB 00800075 00000002 D holdoff 0080008c 00000002 B ledMode 00800062 00000002 D longButtonCB 0080006f 00000002 d longPressTime 00800090 00000002 b longPressTimer 00000068 00000002 T loop 0080006a 00000002 D mode 00000d00 00000002 T setPort 00800064 00000002 D timerCB 0080008a 00000002 B timerCBcount 00800060 00000002 D timerCBtime 0080007d 00000003 D black 0080007a 00000003 D dark 0080006c 00000003 D outColor 00800077 00000003 D wakeColor 00000da6 00000004 T __mulsidi3 00800093 00000004 b powerDownTimer 00800131 00000004 B prevTimer 00800097 00000004 B sleepTimer 0080009b 00000004 b startTime 00800071 00000004 D timeout 00800105 00000004 b timer 00000a7a 00000006 T disAD 00800080 00000006 b pulseCount.1996 0080009f 00000006 b timeBuf 0080010e 00000007 B blinking 00800117 00000007 B pulsing 00000a80 00000008 T enAD 000000d4 0000000c T setColorRGB 00000a88 0000000e T initTimer 0000005a 0000000e T setup 00000044 00000010 T __do_clear_bss 0080011e 00000013 B fading 0000002e 00000016 T __do_copy_data 00000d66 00000018 T __udivmodqi4 00000a5c 0000001e T initIO 00000ce0 00000020 t sendByte 00000d44 00000022 T __mulhi3 00000822 00000022 T __vector_3 0000006c 00000022 T getTimer 00000d7e 00000028 T __udivmodhi4 00000e18 00000036 T __epilogue_restores__ 00000daa 00000036 T __umulsidi3 00000de0 00000038 T __prologue_saves__ 000003e8 00000038 T updateLed 0000027c 0000003a T blink 00000d02 00000042 T sendColor 0000008e 00000046 T tileSetup 00000e4e 0000005c T __muldi3 008000a5 00000060 b times 0000035e 0000008a T pulsingUpdate 000002b6 000000a8 T blinkUpdate 00000a96 000000d4 T hsv2rgb 00000b6a 00000176 T main 000000e0 0000019c T fadeUpdate 00000844 00000218 T __vector_2 00000420 00000402 T __vector_9 The second column is the amount of space used by the symbol.

So just looking at the bottom of our list, we can see that much of the space is being used by the functions main, fadeUpdate, __vector_2 , and __vector_9.

main and fadeUpdate are obvious, but the __vector_x functions are actualy interrupt service routines. The actual mapping of numbers to interrupt sources can be different for different chips, so probably the easiest way to figure out what a given vector is would be to comment out the ISR in your code and then do the above process and see which vector evaporates in size.

You will have to look at each function carefully and individually to see how to possible reduce its size, but at least with this procedure you know where too look!

  • thats neat I didn't know about that tool
    – crowie
    Jan 13, 2017 at 3:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.