Attiny85: Program to big. What can I do?

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hy

I am trying 2 build a controller for my parking heater with a attiny85 (digispark).

So far I got a encoder switch a i2c display and sensor working just fine, but now the flash memory is full.angry

I know about the 2 options to delete the bootloader or simply take a bigger chip (arduino nano already ordered to use as new controller or programmer for the 85 or both)laugh

I am also thinking about writing parts of my program in assembler, but i am using the libraries TinyWire and LiquidChristal_attiny and scince my code contains mainly calls to these libraries and not much other code, i fear that writing my code in assembler wont help much but i realy would need to rewrite the librariessurprise. Is this correct or do you have any other ideas?

Thx

Here the code

#include <avr/io.h>
#include <util/delay.h>
#include <TinyWireM.h>
#include <LiquidCrystal_attiny.h>

#define LCD_ADDR     0x27
#define LCD_WIDTH    16
#define LCD_HEIGHT   2
#define TEMP_ADDR 0x40
#define ENC_A        1
#define ENC_B        4
#define OUT_1        3
#define OUT_2        5

#define delay_time 160 //in sec
#define delay_time_on 5  //in sec
#define menu_len 6
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_WIDTH, LCD_HEIGHT); // set address & 16 chars / 2 lines

volatile int8_t enc_old = 2;  //encoder state memory
volatile int8_t Enc_turn = 0;   //how often encoder has been turned
volatile int8_t Enc_sw = 0;   //if encoder has been pushed

int16_t temp=999;  //ist-temp*10
uint8_t temp_retry=0,temp_state=0;
int16_t t_lim = 150, t_on=60,t_off=60; //soll-temp*10
uint8_t t_hyst=10;
uint8_t state=0;  //0=ports off, 1=1 port on, 2= 2 ports on
volatile unsigned long t_activ=millis();  //time of last encoder state change
int8_t pos_m=1;
char* Menu[menu_len] = {"Exit   ","Temp   ", "OnTime ", "OffTime", "Repeat ", "Hyst.  "};
uint8_t m_state[4]={1,0,0,0};

uint8_t i2c_w8(uint8_t addr,uint8_t data) //send i2c command
{ TinyWireM.beginTransmission(addr);
  TinyWireM.send(data);
  uint8_t r = TinyWireM.endTransmission();
  return r;
};

static inline void initTimer(void)
{
  TCCR1 |= (1 << CTC1);  // clear timer on compare match
  TCCR1 |= (1 << CS13) | (1 << CS12) | (1 << CS11) | (1 << CS10); //clock prescaler 8192
  OCR1C = 1; // compare match value
  TIMSK |= (1 << OCIE1A); // enable compare match interrupt
  sei();
};

ISR(TIMER1_COMPA_vect)  //timed interrupt to read encoder state
{
  enc_old = (enc_old & 0b110000) | (enc_old & 0b11) << 2 | ((PINB >> ENC_A & 1) << 1) | (PINB >> ENC_B & 1);
  if (enc_old == 0b001101)
  { Enc_turn++;
    enc_old |= 0b010000;
    t_activ=millis();
  }
  else if (enc_old == 0b001110 )
  { Enc_turn--;
    enc_old |= 0b010000;
    t_activ=millis();
  }
  else if (enc_old == 0b001100) {Enc_sw = 1;t_activ=millis();}
  else if ((enc_old & 0b110011) == 0b010000) enc_old &= 0b001111;
};

void setup(void)
{ //TinyWireM.begin();
  lcd.init();
  lcd.backlight();
  i2c_w8(TEMP_ADDR,0xFE); //sensor reset
  DDRB|=1<<ENC_A+1<<ENC_B;
  PORTB|=1<<ENC_A+1<<ENC_B;
  initTimer();
};

void show_val(int16_t val,uint8_t dec,char* unit)   //show int16 as float
{ uint8_t pos;
  int8_t rval;
  if (dec) 
  { val=val/pow(10,dec);
    rval=val%(int16_t)pow(10,dec);
  };
  if(val>99) pos=0;
  else if (val>9) pos=1;
  else pos=2;
  if (val<0) { pos--; rval=-rval;};
  while(pos--)
    lcd.print(" ");
  lcd.print(val);
  if (dec)
  { lcd.print(".");
    lcd.print(rval);
  };
  lcd.print(unit);
};
uint16_t set_val(int16_t val,uint8_t dec, uint8_t step,char* unit)  //show and adjust a numerical value with encoder
{ while (true)
  { lcd.setCursor(4,1);
    show_val(val,dec,unit);
    if (Enc_sw)
    { Enc_sw=0;
      return val;
    }
    else
    { val+=step*Enc_turn;
      Enc_turn=0;
    }
    delay(100); 
  };   
}

inline uint8_t measure()   //read temperature sensor (call repeatedly until result)
{   uint8_t s;
    temp_retry++;
    if (temp_retry%20==0) 
    { i2c_w8(TEMP_ADDR,0xFE); //reset
      temp_state=0;};
    if (temp_state==0)
    {  i2c_w8(TEMP_ADDR,0xF3);  //start measurement
      temp_state=1;}
    else temp_state=TinyWireM.requestFrom(TEMP_ADDR,2); //read temperature
    if(!temp_state) 
    { uint16_t hi=TinyWireM.receive()<<8;
      hi+=TinyWireM.receive();
      temp=(int16_t)(0.026813*hi-468.5);      
      temp_retry=0;
      return 0;
    }
    else if(temp_retry>60)
    { temp=-999;
      temp_state=0;
      temp_retry=0;
      return 2;};
     return 1;
};

void show_tt()    //display actual temperature, set temperature and timing
{ lcd.setCursor(0,0);
  lcd.print((int)state);
  lcd.print(" T");
  show_val(temp,1,""); 
  if (m_state[0]) 
  { lcd.print(" >");
    show_val(t_lim,1,"C");};
  if (m_state[1]) 
  { lcd.setCursor(2,1);
    lcd.print("t1");
    show_val(t_on,0,"m");};
  if (m_state[2]) 
  { lcd.setCursor(2,1);
    lcd.print("t2");
    show_val(t_on,0,"m");};  
};

inline void set_state()     //swich state/ports according to temperature
{ if (temp <= t_lim) state=1;
  if (temp<=t_lim-t_hyst) state=2;
  else if (temp>=t_lim+t_hyst) state=0; 
  //PORTB|=(PORTMASK&(state|state<<1));
}

void loop() { 
  measure();
  show_tt();
  set_state();
  if(Enc_sw or Enc_turn)    //if user input
  { Enc_sw=0;
    if (pos_m<0)    //if display was off, display on
    {lcd.init(); lcd.backlight(); pos_m=0; }
    else if(Enc_turn)   //if encoder turned adjust temperatuer setting
    {
      t_lim+=5*Enc_turn;
      Enc_turn=0;
    }
    else                //if encoder pushed show menu
    { lcd.clear();   
      while (1)
      { lcd.setCursor(3, 0);
        lcd.print(Menu[pos_m]); //show menu item in line 1
        lcd.setCursor(13, 0);
        if(pos_m>0 && pos_m<5)
        { if(m_state[pos_m-1] && pos_m<5) lcd.print(" On");   //show state of menu item
          else lcd.print("Off");}
        else lcd.print("   ");
        lcd.setCursor(3, 1);
        if(pos_m<menu_len-1) lcd.print(Menu[pos_m+1]);    //show menu item of line 2
        else lcd.print("         ");
        delay(100);
        if (Enc_sw)   //if pushbutton execute menuitem
        { Enc_sw=0;
          lcd.clear();
          if(pos_m==0) break;       //quit menu
          else if (pos_m<5) 
             m_state[pos_m-1]^=1;   //toggle options (temp mode, time mode,...) 
          else if(pos_m==5)
          { lcd.setCursor(13, 0);   //adjust hysteresis
            set_val(t_hyst,1,1,"C");
            if(t_hyst<0) t_hyst=-t_hyst;}
          Enc_turn=pos_m;
          lcd.clear();
        }
        else      //if Enc_turn is out of limits (number of menu itims)
        { pos_m=Enc_turn; 
          if (pos_m<0) pos_m=0;
          else if (pos_m>menu_len-1) pos_m=menu_len-1;
        }
        Enc_turn=pos_m;
     if(millis()-t_activ>5000) break; //
      };
      lcd.clear();
    };
  };
  delay(100); 
  if(millis()-t_activ>20000) //backlight off if inactive
   { lcd.noBacklight();
     lcd.init();
     pos_m=-1;
   };
};
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is worthy of refactoring:

  if (dec) 
  { val=val/pow(10,dec);
    rval=val%(int16_t)pow(10,dec);
  };

If you are just after the integers 1, 10, 100 etc. a lookup table or even switch-case could be shorter that pow().

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

blubbersprudel wrote:
...  to delete the bootloader ...
or an alternate bootloader :

Ralph's, 64B or 256B

TinySafeBoot, 550B

V-USB, 1418B

blubbersprudel wrote:
... or do you have any other ideas?

  • Try other AVR C compilers to squeeze (MPLAB XC8 AVR is an alternate AVR GCC, FSF AVR GCC v<something>, etc)
  • IAR EWAVR Kickstart though may be two 4KB applications (trampoline, function pointers, other?) or, if fortunate, bootloader+application

 


GitHub - nerdralph/picoboot: Automatically exported from code.google.com/p/picoboot

TinySafeBoot - A tiny, safe and flexible AVR-Bootloader for ATtinys and ATmegas

GitHub - micronucleus/micronucleus: ATTiny usb bootloader with a strong emphasis on bootloader compactness.

MPLAB- XC Compilers | Microchip Technology

IAR Embedded Workbench - AVR

 

edit : typo

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Sat. Nov 30, 2019 - 12:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Holy shit! replacing pow() saved more than 1K!cheeky

Wouldnt have thought that!

Thanks a lot!

int8_t rval,dec1=dec;

  uint8_t pot=1;
  while (dec1--)
  { val=val/10;
    pot*=10;
  };
  rval=val%pot;

 

Last Edited: Sat. Nov 30, 2019 - 12:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An alternat bootloader is a great idea also!

Didnt know they existed.surprise I certainly will look into it.

Thx.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

blubbersprudel wrote:

      temp=(int16_t)(0.026813*hi-468.5);      

Don't use floating point, and I would think you will save another big chunk, assuming nowhere else uses floating point now you got rid of the pow() function.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

which compiler options do you use ?

 

try both -O2 and -Os

 

how big is the code (I assume it compile just with a size more than 100%).

 

in general don't use float,  

0.026813

is close to :

7/256 = 0,02734375

or better

1757/65536 = 0,02680969

so first multiply with 7 and then div with 256 (which is nothing other than a offset of a byte).

(just make sure that the multiply don't overflow ) 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice!! another 800 Bytes. Thxlaugh

Und ich benutze die Arduino IDE, da hab ich bisher noch keine Compilereinstellungen gefunden.

Last Edited: Sat. Nov 30, 2019 - 05:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I'm glad to hear that you freed up some more memory for your program.

 

I think, however, that the real learning point is this:

Start with a big micro when you start a new project!

 

It is very frustrating to have a project mostly done, and run out of memory, (or I/O Pins, or processor speed, or, or, or....).

 

If you are only building a few boards, then the cost difference is minimal, and it saves you running into this problem.

 

If you are going to manufacture a large number of boards, then after you have a working prototype you can begin to optimize the hardware, i.e. see if you can fit it into a less expensive (presumably smaller) micro.

 

If you are building an in-the-ear hearing aid, size matters.

If you are building a heating controller, size likely doesn't matter.

 

Good Luck with your project!

 

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
Start with a big micro when you start a new project

Absolutely!

 

Premature optimisation is a root of all kinds of evils.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

come on the job OP need should take less than 2K of flash.

Is the next suggestion to start with a file system  and linux kernel, and therefor need 512K+ flash and about same amount of RAM before it can turn a LED on. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I like to not get too excessive, maybe allow a 2x factor for some elbow room....having too much room tends to lead to sloppiness..of course, I'm willing to admit, we might agree to say "who cares", the chip will just be $1.23 even if it is 10x of the space we need.

 

A company called me about some LCD menus they had developed & now needed to fix some bugs,  add additional menus,  and code new features to go with them.  They were already using 95% of the space & wanted to know if I'd take it on, select a new (bigger) chip & take care of everything.    I called a week later to let them know I had found & fixed their bugs.  They asked how the new chip selection was going & I told them I was able to reduce code space by almost 50%.  They asked if that might leave enough room to include all of the new things (and avoid a possible board update).  They were amazed to learn that all the new menus & features were already implemented & the whole thing took less than half of the code space they started with.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My current project is a 4 digit seven segment driver. Included in the code is a demo counter from 0 to 9999 , a delay loop plus a simple dimming control for the LEDs, timer1 for demo counter incrementing every 200ms or so, a 74hc595 for the segments. All of  this in 300 bytes...    So 8k  is huge, just depends on your overhead.  I use ASM only because I'm not smart enough to use C, but I do like the benefits.  The libraries (especially in arduino code for example) are convenient but large.  Just sayin... not trying to start a war

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
Is the next suggestion to start with a file system  and linux kernel, and therefor need 512K+ flash and about same amount of RAM before it can turn a LED on. 

 

These days you need a Cortex M7 @600MHz to blink LEDs.

https://www.pjrc.com/store/teens...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I'd give students a schematic diagram with about 15 various gates & flip-flops & ask them to study what it did & build it up to verify.  I also mentioned that they should simplify wherever possible.  Most jumped right on it and soon had a breadboard overflowing with parts and connections.  The more thoughtful students were able to build it using only 2 wires and nothing else.   I think software often follows a similar path.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

which compiler options do you use ?

try both -O2 and -Os

 

Are you using the Arduino IDE to do the compiles?   you've got setup() and loop(), but also utils/delay.cm si it's not obvious.

The Arduino libraries have gotten to the point where you need to have -ffunction-sections -fdata-sections -Wl,-gc-sections in your compile/link...