0

Iam making an awesome christmas present a custom pcb badge. Everything works great with hooked up uno, but its heart is actually a Attiny13a, where I get ONLY around 1024bytes (mine code has 1944) 64bytes of variabels (I somehow managed to make few things copy on run so i barely get to it at 59 bytes)

The thing is how can I make this even smaller??

void setup() {
  pinMode(3, INPUT_PULLUP);
}

void charlie(int a[])
{

  if(a[0] < 0){

    pinMode(5, INPUT);
  }else
  {
    pinMode(5, OUTPUT);
    digitalWrite(5, a[0]);    
  }   
  if(a[1] < 0){

    pinMode(6, INPUT);
  }else
  {
    pinMode(6, OUTPUT);
    digitalWrite(6, a[1]);    
  }
  if(a[2] < 0){

    pinMode(2, INPUT);
  }else
  {
    pinMode(2, OUTPUT);
    digitalWrite(2, a[2]);    
  }
  if(a[3] < 0){

    pinMode(4, INPUT);
  }else
  {
    pinMode(4, OUTPUT);
    digitalWrite(4, a[3]);    
  }
}

void copy(int* src, int* dst, int len) {
    memcpy(dst, src, sizeof(src[0])*len);
}
void resetP()
{
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(2, INPUT);
  pinMode(4, INPUT);  
}
void loop() {

  int koz1[4] = {-2,LOW,HIGH,-2};
  //int koz2[4] = {-2,HIGH,LOW,-2};

  int koz2[4];
  copy(koz1, koz2, 4);
  koz2[1] = HIGH;
  koz2[2] = LOW;

  int koz3[4] = {LOW,HIGH,-2,-2};
  //int koz4[4] = {HIGH,LOW,-2,-2};
  int koz4[4];
  copy(koz3, koz4, 4);
  koz4[0] = HIGH;
  koz4[1] = LOW;


  int koz5[4] = {LOW, -2,-2,HIGH};
  //int koz6[4] = {HIGH, -2,-2,LOW};
  int koz6[4];
  copy(koz5, koz6, 4);
  koz4[0] = HIGH;
  koz4[3] = LOW;

  int cap[4] = {-2, HIGH,-2,LOW};


  int oko1[4] = {LOW, -2,HIGH,-2};
  //int oko2[4] = {HIGH, -2,LOW,-2};

  int oko2[4];
  copy(oko1, oko2, 4);
  oko2[0] = HIGH;
  oko2[2] = LOW;


  // put your main code here, to run repeatedly:

  static long oldTime = 0;  
  long Ctime = millis();  
  long diffT = Ctime - oldTime;
  oldTime = Ctime;


  static bool holdButt = false;
  static long holdTime = 0;
  static long shortPressATime = 0;
  //static long longPressATime = 0;
  static bool kozichOn = true;

  //charlie(oko1);
  //charlie(oko2);


  if(digitalRead(3) == LOW)
  {
    holdButt = true;
    holdTime = holdTime + diffT;
  }
  else if (holdButt && digitalRead(3) == HIGH){
    holdButt = false;
    if(holdTime > 1000){
      //longPressATime = 5000;
      kozichOn = !kozichOn;
    }
    else
    {      
      shortPressATime = 5000;
    }
    holdTime = 0;
  }

  if(shortPressATime > 0)
  {
    shortPressATime = shortPressATime - diffT;
    /*charlie(koz1);
    charlie(koz2);
    charlie(koz3);
    charlie(koz4);
    charlie(koz5);
    charlie(koz6);
    charlie(cap);
    */
    if(((round(shortPressATime/100) / 2) & 1) == 0){
      charlie(oko1);
      charlie(oko2);
      resetP();
    }
    else{

    }

  }

  /*
  else if (longPressATime > 0)
  { 
    longPressATime = longPressATime - diffT;   
    charlie(oko1);
    delay(5000/longPressATime);

    charlie(oko2);
    delay(5000/longPressATime);

    longPressATime = longPressATime - 5000/longPressATime;
    longPressATime = longPressATime - 5000/longPressATime;
  }*/
  else{
    if(kozichOn){
      charlie(koz1);
      charlie(koz2);
      charlie(koz3);
      charlie(koz4);
      charlie(koz5);
      charlie(koz6);
      charlie(cap);
    }
    charlie(oko1);
    charlie(oko2);
  }
  resetP();
}

EDIT: Guys you are awesome together with those on arduino forums it looks like I finally achieved it, I will try to optimize little bit more as its 1010bytes 98%. But I thank you all. You teached me in 1 hour more practical things, than I could read in a week.

void setup() {
  pinMode(3, INPUT_PULLUP);
}

void charlie(byte a[])
{



  if(a[0] == 0x0F){

    pinMode(5, INPUT);
  }else
  {
    pinMode(5, OUTPUT);
    digitalWrite(5, a[0]);    
  }   
  if(a[1] == 0x0F){

    pinMode(6, INPUT);
  }else
  {
    pinMode(6, OUTPUT);
    digitalWrite(6, a[1]);    
  }
  if(a[2] == 0x0F){

    pinMode(2, INPUT);
  }else
  {
    pinMode(2, OUTPUT);
    digitalWrite(2, a[2]);    
  }
  if(a[3] == 0x0F){

    pinMode(4, INPUT);
  }else
  {
    pinMode(4, OUTPUT);
    digitalWrite(4, a[3]);    
  }
}
/*
void copy(int* src, int* dst, int len) {
    memcpy(dst, src, sizeof(src[0])*len);


}
*/
/*
void resetP()
{
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(2, INPUT);
  pinMode(4, INPUT);  
}
*/

void loop() {

  byte koz1[4] = {0x0F,LOW,HIGH,0x0F};
  //int koz2[4] = {null,HIGH,LOW,null};

  byte koz2[4];
  //copy(koz1, koz2, 4);
  memcpy(koz2, koz1, sizeof(koz1[0])*4);
  koz2[1] = HIGH;
  koz2[2] = LOW;

  byte koz3[4] = {LOW,HIGH,0x0F,0x0F};
  //int koz4[4] = {HIGH,LOW,2,2};
  byte koz4[4];
  //copy(koz3, koz4, 4);
  memcpy(koz4, koz3, sizeof(koz3[0])*4);
  koz4[0] = HIGH;
  koz4[1] = LOW;


  byte koz5[4] = {LOW, 0x0F,0x0F,HIGH};
  //int koz6[4] = {HIGH, 2,2,LOW};
  byte koz6[4];
  //copy(koz5, koz6, 4);
  memcpy(koz6, koz5, sizeof(koz5[0])*4);
  koz4[0] = HIGH;
  koz4[3] = LOW;

  byte cap[4] = {0x0F, HIGH,0x0F,LOW};


  byte oko1[4] = {LOW, 0x0F,HIGH,0x0F};
  //int oko2[4] = {HIGH, 2,LOW,2};

  byte oko2[4];
  //copy(oko1, oko2, 4);
  memcpy(oko2, oko1, sizeof(oko1[0])*4);
  oko2[0] = HIGH;
  oko2[2] = LOW;


  // put your main code here, to run repeatedly:

  static uint16_t oldTime = 0;  
  uint16_t Ctime = millis();  
  uint16_t diffT = Ctime - oldTime;
  oldTime = Ctime;


  static bool holdButt = false;
  static uint16_t holdTime = 0;
  static uint16_t shortPressATime = 0;
  //static long longPressATime = 0;
  static bool kozichOn = true;

  //charlie(oko1);
  //charlie(oko2);


  if(digitalRead(3) == LOW)
  {
    holdButt = true;
    holdTime = holdTime + diffT;
  }
  else if (holdButt && digitalRead(3) == HIGH){
    holdButt = false;
    if(holdTime > 1000){
      //longPressATime = 5000;
      kozichOn = !kozichOn;
    }
    else
    {      
      shortPressATime = 5000;
    }
    holdTime = 0;
  }




  if(shortPressATime > 0)
  {
    shortPressATime = shortPressATime - diffT;
    if(((uint16_t(shortPressATime/128) / 2) & 1) == 0){
      charlie(oko1);
      charlie(oko2);

    }
    else{

    }



  }
  else{
    if(kozichOn){
      charlie(koz1);
      charlie(koz2);
      charlie(koz3);
      charlie(koz4);
      charlie(koz5);
      charlie(koz6);
      charlie(cap);
    }
    charlie(oko1);
    charlie(oko2);
  }
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(2, INPUT);
  pinMode(4, INPUT);
}
1
  • what is the desired functionality?
    – jsotola
    Commented Dec 13, 2019 at 22:22

2 Answers 2

2

The first thing I would try is to get rid of the Arduino core, and program instead at the avr-libc level:

  • use direct port access instead of digitalRead()/digitalWrite()
  • setup a timer and measure time in units of timer overflows instead of using millis().

Also, use int8_t for all small ints (especially in arrays), and uint16_t for time management if you don't need to handle durations larger than 65,535 time units.


Edit: Here is my attempt at translating your code to plain C + avr-libc. Note that, as I don't know the pinout of the core you are using (what is pin 6?), I am making one up. I also implemented Michel Keijzers suggestion about factoring duplicated code into a function, which I called set_pin(). Completely untested:

#include <stdbool.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#define INPUT 0x0f
#define LOW   0x00
#define HIGH  0x01

// Configure Timer 0 to count milliseconds, assuming F_CPU = 8 MHz.
static void init_timer() {
  OCR0A  = 125 - 1;     // period = 125*64 = 8000 CPU cycles
  TCCR0A = _BV(WGM01);  // CTC, TOP = OCR0A
  TCCR0B = _BV(CS00)    // clock @ F_CPU/64
         | _BV(CS01);   // ditto
  TIMSK0 = _BV(TOIE0);  // enable timer overflow interrupt
}

volatile uint16_t millis_counter;

// Count a millisecond on each timer overflow.
ISR(TIM0_OVF_vect) {
  ++millis_counter;
}

// Read the milliseconds counter avoiding race conditions.
static uint16_t millis() {
  uint16_t millis_counter_copy;
  ATOMIC_BLOCK(ATOMIC_FORCEON) {
    millis_counter_copy = millis_counter;
  }
  return millis_counter_copy;
}

static void setup() {
  PORTB |= _BV(PB4);  // pullup on PB4
  init_timer();
  sei();
}

// Set a pin as either INPUT, LOW or HIGH.
static void set_pin(uint8_t pin_mask, uint8_t state) {
  if (state == INPUT) {
    DDRB &= ~pin_mask;     // INPUT
    PORTB &= ~pin_mask;    // no pullup
  } else {
    if (state)
      PORTB |= pin_mask;   // HIGH
    else
      PORTB &= ~pin_mask;  // LOW
    DDRB |= pin_mask;      // OUTPUT
  }
}

static void charlie(uint8_t a[])
{
  set_pin(_BV(PB0), a[0]);
  set_pin(_BV(PB1), a[1]);
  set_pin(_BV(PB2), a[2]);
  set_pin(_BV(PB3), a[3]);
}

void loop() {

  uint8_t koz1[4] = {INPUT,LOW,HIGH,INPUT};

  uint8_t koz2[4];
  memcpy(koz2, koz1, sizeof(koz1[0])*4);
  koz2[1] = HIGH;
  koz2[2] = LOW;

  uint8_t koz3[4] = {LOW,HIGH,INPUT,INPUT};
  uint8_t koz4[4];
  memcpy(koz4, koz3, sizeof(koz3[0])*4);
  koz4[0] = HIGH;
  koz4[1] = LOW;

  uint8_t koz5[4] = {LOW, INPUT,INPUT,HIGH};
  uint8_t koz6[4];
  memcpy(koz6, koz5, sizeof(koz5[0])*4);
  koz4[0] = HIGH;
  koz4[3] = LOW;

  uint8_t cap[4] = {INPUT, HIGH,INPUT,LOW};

  uint8_t oko1[4] = {LOW, INPUT,HIGH,INPUT};
  uint8_t oko2[4];
  memcpy(oko2, oko1, sizeof(oko1[0])*4);
  oko2[0] = HIGH;
  oko2[2] = LOW;

  static uint16_t oldTime = 0;  
  uint16_t Ctime = millis();  
  uint16_t diffT = Ctime - oldTime;
  oldTime = Ctime;

  static bool holdButt = false;
  static uint16_t holdTime = 0;
  static uint16_t shortPressATime = 0;
  static bool kozichOn = true;

  if (bit_is_clear(PINB, PB4))
  {
    holdButt = true;
    holdTime = holdTime + diffT;
  }
  else if (holdButt && bit_is_set(PINB, PB4)){
    holdButt = false;
    if(holdTime > 1000){
      kozichOn = !kozichOn;
    }
    else
    {      
      shortPressATime = 5000;
    }
    holdTime = 0;
  }

  if(shortPressATime > 0)
  {
    shortPressATime = shortPressATime - diffT;
    if(((shortPressATime/128 / 2) & 1) == 0){
      charlie(oko1);
      charlie(oko2);
    }
  }
  else{
    if(kozichOn){
      charlie(koz1);
      charlie(koz2);
      charlie(koz3);
      charlie(koz4);
      charlie(koz5);
      charlie(koz6);
      charlie(cap);
    }
    charlie(oko1);
    charlie(oko2);
  }
  DDRB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3));
}

int main(void) {
  setup();
  for (;;)
    loop();
}

Compiling with avr-gcc 5.4.0, I get 642 bytes of flash and 31 bytes of RAM (data + BSS, not counting the stack).

3
  • I suggest ditching the ArduinoIDE and moving to Atmel Studio. I had good experience using it with USBasp and using avrdude (supplied as a part of Arduino IDE) to upload the program to ATTiny13a directly. Commented Dec 13, 2019 at 23:06
  • Made it thank you, thank for suggestion, ive heard of it, but it seems even harder to me, like i know c# and similiar bcz of game development, but idek what atmel studio uses, but i will give it a try in some upcoming project. Again big thanks. Commented Dec 13, 2019 at 23:28
  • I will try your translation, I was prototyping with arduino so it should have been 0,1,2,4 plexing 3 - pullup button Commented Dec 14, 2019 at 10:03
-1

When I compile it, I get:

Sketch uses 2630 bytes (8%) of program storage space. Maximum is 32256 bytes.
Global variables use 64 bytes (3%) of dynamic memory, leaving 1984 bytes for local variables. Maximum is 2048 bytes.

I removed all commented lines, and changed the integers to bytes (according to Edgar Bonet's answer as a start.

Than I get the code (added at end) and the following results:

Sketch uses 2432 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 24 bytes (1%) of dynamic memory, leaving 2024 bytes for local variables. Maximum is 2048 bytes.

According to the specs, an AtTiny85 has 8 KB Program memory and 512 bytes SRAM, so you should be safe.

Note however, if you want to count SRAM memory (24 bytes counted by the IDE), but also you have to add:

  1. Bytes for the calling depth of functions (e.g. loop calls copy and this calls memcpy, the total memory is for copy: + 2 (byte*) + 2 (byte*) + 1 (byte) bytes, and for memcpy: + dst (2) + src (2) + sizeof (1) byte. You have to check the most 'worse' call (so check the deepest invocation with all arguments added). This is all added to the so-called stack.
  2. Also you have to add the cost of local variables (once per function), because these are also added to the stack.
  3. If you use dynamic variables (malloc) you also have to add them, but this is not used in your sketch.

The bytes used for items 1 to 3 are not mentioned (because these are allocated at runtime).

void setup() {
  pinMode(3, INPUT_PULLUP);
}

void charlie(byte a[])
{
  if(a[0] < 0){

    pinMode(5, INPUT);
  }else
  {
    pinMode(5, OUTPUT);
    digitalWrite(5, a[0]);    
  }   
  if(a[1] < 0){

    pinMode(6, INPUT);
  }else
  {
    pinMode(6, OUTPUT);
    digitalWrite(6, a[1]);    
  }
  if(a[2] < 0){

    pinMode(2, INPUT);
  }else
  {
    pinMode(2, OUTPUT);
    digitalWrite(2, a[2]);    
  }
  if(a[3] < 0){

    pinMode(4, INPUT);
  }else
  {
    pinMode(4, OUTPUT);
    digitalWrite(4, a[3]);    
  }
}

void copy(byte* src, byte* dst, byte len) {
    memcpy(dst, src, sizeof(src[0])*len);
}

void resetP()
{
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(2, INPUT);
  pinMode(4, INPUT);  
}
void loop() {

  byte koz1[4] = {-2,LOW,HIGH,-2};
  byte koz2[4];
  copy(koz1, koz2, 4);
  koz2[1] = HIGH;
  koz2[2] = LOW;

  byte koz3[4] = {LOW,HIGH,-2,-2};
  byte koz4[4];
  copy(koz3, koz4, 4);
  koz4[0] = HIGH;
  koz4[1] = LOW;


  byte koz5[4] = {LOW, -2,-2,HIGH};
  byte koz6[4];
  copy(koz5, koz6, 4);
  koz4[0] = HIGH;
  koz4[3] = LOW;

  byte cap[4] = {-2, HIGH,-2,LOW};
  byte oko1[4] = {LOW, -2,HIGH,-2};

  byte oko2[4];
  copy(oko1, oko2, 4);
  oko2[0] = HIGH;
  oko2[2] = LOW;

  static long oldTime = 0;  
  long Ctime = millis();  
  long diffT = Ctime - oldTime;
  oldTime = Ctime;

  static bool holdButt = false;
  static long holdTime = 0;
  static long shortPressATime = 0;
  static bool kozichOn = true;

  if(digitalRead(3) == LOW)
  {
    holdButt = true;
    holdTime = holdTime + diffT;
  }
  else if (holdButt && digitalRead(3) == HIGH){
    holdButt = false;
    if(holdTime > 1000){
      //longPressATime = 5000;
      kozichOn = !kozichOn;
    }
    else
    {      
      shortPressATime = 5000;
    }
    holdTime = 0;
  }

  if(shortPressATime > 0)
  {
    shortPressATime = shortPressATime - diffT;

    if(((round(shortPressATime/100) / 2) & 1) == 0){
      charlie(oko1);
      charlie(oko2);
      resetP();
    }
    else{
    }
  }
  else{
    if(kozichOn){
      charlie(koz1);
      charlie(koz2);
      charlie(koz3);
      charlie(koz4);
      charlie(koz5);
      charlie(koz6);
      charlie(cap);
    }
    charlie(oko1);
    charlie(oko2);
  }
  resetP();
}
7
  • Iam actually using Attiny13a which has only 1024bytes, thru using some help from arduino forum i got to great 1218bytes which is super close to the goal, I will now try to implement this. EDIT: With your improvement Iam at 1074bytes super close. Commented Dec 13, 2019 at 22:37
  • So you need to reduce the code size (Flash), not SRAM? These are two different things. Commented Dec 13, 2019 at 22:48
  • Sorry but iam back at the problem I need to diffirentiate 3 values and when I use bytes I need to change the function so it ifs(==2) but then Iam back at 1246 Commented Dec 13, 2019 at 22:49
  • Not sure what you say (also you didn't give an answer I think) ... You could copy the duplicated code to a single function and call it with parameters, that saves you some Flash space. Commented Dec 13, 2019 at 23:02
  • Made it. Thank you Commented Dec 13, 2019 at 23:26

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.