Electronic – arduino – Porting Code from Arduino Uno/ATMega328P to ATTiny85 not working, too many variables

arduinoattiny

I have an Arduino Uno and am running a sketch with lots of variables (23 constant char arrays that have 54 object in each array). Along with all these variables there is lots of if statements. The project controls an RGB LED neopixel strip where each LED is independent (WS2812 based). This code runs fine under the Uno and the LEDs flash as expected. However when I put the code on the ATtiny85 they all flash randomly or some turn on and stay on. The code compiles and loads with no errors on the Attiny85. I am not sure what is going on that would cause it to not work? Any help is much appreciated!

I am defining the arrays like so const char arrayName[] = and I was wondering if that is the issue because there is so many?

The code is to do an RGB LED word clock. The reason I would like to use an Attiny is 1. I already have it (but I could buy something else that is not totally unacceptable). and 2. it is small enough to fit in the case.

I could buy another chip, but would like to understand the limitations and why it doesn't work before buying another chip.

If it is not possible to get this working on the Attiny85 then is there an alternative small chip that would work?

Below is the code – with the exception of right now it is setup for serial println instead of the actual RGB LED output. Is there a more efficient way that might solve the problem?

const char it[] =      {'1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char is[] =      {'0','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char mten[] =    {'0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char half[] =    {'0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char twenty[] =  {'0','0','0','0','0','0','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char quarter[] = {'0','0','0','0','0','0','0','0','0','1','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char mfive[] =   {'0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char minutes[] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char two[] =     {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char one[] =     {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char to[] =      {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char past[] =    {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char three[] =   {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char four[] =    {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char five[] =    {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char eight[] =   {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char seven[] =   {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char six[] =     {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0','0','0'};
const char nine[] =    {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0','0','0'};
const char ten[] =     {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','0','0','0','0','0','0','0','0','0'};
const char eleven[] =  {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','0','0','0','0','0','0'};
const char oclock[] =  {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1','0','0','0'};
const char twelve[] =  {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','1','1','1'};

char pins[55] =  {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','\0'};

int red = 50;
int grn = 0;
int blu = 0;

uint8_t oldminute = 0;

void setup () {  

}

void editPins(const char add[]) {
  for (int a = 0; a < 54; a++) {
    if (add[a] == '1') {
      pins[a] = '1';
    }
  }
}

void loop () {
  uint8_t hourvalue, minutevalue;

  hourvalue = 8;
  minutevalue = 13;

  for (int x = 0; x < 55; x++) {
    pins[x] = '0';
  }

    if ((minutevalue>4) && (minutevalue<10)) { 
      editPins(mfive); 
      editPins(minutes);
    } 
    if ((minutevalue>9) && (minutevalue<15)) { 
      editPins(mten); 
      editPins(minutes);
    }
    if ((minutevalue>14) && (minutevalue<20)) {
      editPins(quarter);
    }
    if ((minutevalue>19) && (minutevalue<25)) { 
      editPins(twenty);
      editPins(minutes);
    }
    if ((minutevalue>24) && (minutevalue<30)) { 
      editPins(twenty);
      editPins(mfive);
      editPins(minutes);
    }  
    if ((minutevalue>29) && (minutevalue<35)) {
    editPins(half);
    }
    if ((minutevalue>34) && (minutevalue<40)) { 
      editPins(twenty);
      editPins(mfive);
      editPins(minutes);
    }  
    if ((minutevalue>39) && (minutevalue<45)) { 
      editPins(twenty);
      editPins(minutes);
    }
    if ((minutevalue>44) && (minutevalue<50)) {
      editPins(quarter);
    }
    if ((minutevalue>49) && (minutevalue<55)) { 
      editPins(mten);
      editPins(minutes);
    } 
    if (minutevalue>54) { 
      editPins(mfive);
      editPins(minutes);
    }

    if ((minutevalue <5)) {
      if (hourvalue==1) { 
        editPins(one);
      }
      if (hourvalue==2) { 
        editPins(two);
      }
      if (hourvalue==3) { 
        editPins(three);
      }
      if (hourvalue==4) { 
        editPins(four);
      }
      if (hourvalue==5) { 
        editPins(five);
      }
      if (hourvalue==6) { 
        editPins(six);
      }
      if (hourvalue==7) { 
        editPins(seven);
      }
      if (hourvalue==8) { 
        editPins(eight);
      }
      if (hourvalue==9) { 
        editPins(nine);
      }
      if (hourvalue==10) { 
        editPins(ten);
      }
      if (hourvalue==11) { 
        editPins(eleven);
      }
      if (hourvalue==12) { 
        editPins(twelve);
      }
      editPins(oclock);
    } else
      if ((minutevalue < 35) && (minutevalue >4)) {
        editPins(past);
        if (hourvalue==1) { 
          editPins(one);
        }
        if (hourvalue==2) { 
          editPins(two);
        }
        if (hourvalue==3) { 
          editPins(three);
        }
        if (hourvalue==4) { 
          editPins(four);
        }
        if (hourvalue==5) { 
          editPins(five);
        }
        if (hourvalue==6) { 
          editPins(six);
        }
        if (hourvalue==7) { 
          editPins(seven);
        }
        if (hourvalue==8) { 
          editPins(eight);
        }
        if (hourvalue==9) { 
          editPins(nine);
        }
        if (hourvalue==10) { 
          editPins(ten);
        }
        if (hourvalue==11) { 
          editPins(eleven);
        }
        if (hourvalue==12) { 
          editPins(twelve);
        }

      } else {
        // if we are greater than 34 minutes past the hour then display
        // the next hour, as we will be displaying a 'to' sign
        editPins(to);
        if (hourvalue==1) { 
          editPins(two);
        }
        if (hourvalue==2) { 
          editPins(three);
        }
        if (hourvalue==3) { 
          editPins(four);
        }
        if (hourvalue==4) { 
          editPins(five);
        }
        if (hourvalue==5) { 
          editPins(six);
        }
        if (hourvalue==6) { 
          editPins(seven);
        }
        if (hourvalue==7) { 
          editPins(eight);
        }
        if (hourvalue==8) { 
          editPins(nine);
        }
        if (hourvalue==9) { 
          editPins(ten);
        }
        if (hourvalue==10) { 
          editPins(eleven);
        }
        if (hourvalue==11) { 
          editPins(twelve);
        }
        if (hourvalue==12) { 
          editPins(one);
        }
      } 

      for (int x = 0; x < 54; x++) {
        if (pins[x]=='1') {
          Serial.println(x);
        }
      }
      delay(5000);
}

Best Answer

First, the difference between the ATmega328p used on the Uno, and the ATtiny is mainly (for your purposes anyway), the amount of memory and flash available. The ATmega328p has 32KB Flash and 2KB SRAM. The ATtiny85 has a fourth of both, at 8KB Flash and 0.5KB SRAM.

Second, while you use the Const keyword, as others have pointed out, this works differently on Arduino/avr-gcc than it does in some other microcontrollers. My personal favorite, the MSP430, uses Const to write the variable as read only, to the code space, not to ram. The Arduino implementation does not. Having read http://arduino.cc/en/Reference/PROGMEM, it seems that the Arduino method of doing this is a bit more complex than I personally care to dive into. But…

Third, your problem mostly lies in the very inefficient char array you are using. It's a memory hog. On top of memory needed for globals, and Arduino library usage, your 23 arrays of 54 chars each takes 1242 Bytes. 1.2KB of memory are taken up, as each char in the char array takes up a minimum of a 8 bit byte! No wonder the ATtiny has a tiny fit! The Arduino Uno itself is at 75% memory capacity with the code you pasted.

There are a few ways to fix it. The first would be the Progmem above, to have these readonly arrays added to Flash and not SRAM (or use the eeprom space, that's another option). At that point, you should be below the 0.5KB SRAM limit.

The second, which might be easier for you to start, would be combining each of the 54 objects in each array, into groups of 8. Since each char takes up 8 bits of memory, and you are wasting 7 bits by only using an object that equals 0 or 1, aka a bit, combining them would save 7 bits each,** for a total usage of 161 bytes!**

const char it[] = {'1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};

Becomes

const char it[] = {B10000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,};

This adds two pad bits at the end, still saving much memory. I used binary notation (B followed by 8 bits), but hex or decimal can be used. The your editPins changes to two embedded for loops:

void editPins(const char add[]) {
  for (int a = 0; a < 7; a++) {
     for (int b = 0; b < 8; b++) {
      if ( (add[a] >> b) & 0x01 == 1) { //(if add[a] bit shifted and 1 equals 1)
         pins[((a + 1) * (b + 1)) - 1] = '1';
      }
     }
   }
 }

My syntax may be off, but recursive loops are fine.

And the Third option, would be a not too complicated array of variables for a for loop. Your arrays are very neat, in that they have all bits set to 1 together, surrounded by 0 bits. I would have an array that goes:

char example[3] = {'number of zeros', 'numbers of ones','number of zeros'}

followed by a set of for loops:

 //set i zeros
 for (int i = 0; i < example[0]; i++) pins[i] = 0;
 //set i ones
 for (int i = example[0]; i < example[1]; i++) pins[i] = 1;
 //set i zeros
 for (int i = example[1]; i < example[2]; i++) pins[i] = 0;

Or the array would have the start index of the 1 bits, and how many 1 bits. The former option uses 3 bytes for each array (69 bytes for all 23 arrays), while the latter uses 2 bytes (46 bytes total)