2
\$\begingroup\$

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);
}
\$\endgroup\$
17
  • \$\begingroup\$ Can we look at the code to see what may be wrong? \$\endgroup\$
    – Ricardo
    Oct 27 '14 at 18:31
  • \$\begingroup\$ I have added the code so that might help? \$\endgroup\$
    – Elmer
    Oct 27 '14 at 19:03
  • \$\begingroup\$ While this appears to be as much an efficient usage problem as a part one, the ATMega328p is available in a 28- or 32-pin VQFN package. Dealing with that by hand should be possible with some practice and a $100 hot air station or $25 hot plate and $10 10x loupe, though it's understandable if you prefer not to tackle that. \$\endgroup\$ Oct 27 '14 at 19:03
  • \$\begingroup\$ So if I used and ATMega328 DIP that should work? I could do that, but I would like to understand the issue. I dont understand why the code compiles with no errors on the Attiny85 but wont run? \$\endgroup\$
    – Elmer
    Oct 27 '14 at 19:05
  • 3
    \$\begingroup\$ Man, your char array is a memory hog. 23 x 54 chars (8 bits) that's 1.2 KILOBYTES of memory for what should be 162 bytes of actual data (single bits). You might want to use boolen instead of char, or combine every 8 bits of the char rray into a single byte. \$\endgroup\$
    – Passerby
    Oct 27 '14 at 19:51
1
\$\begingroup\$

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)

\$\endgroup\$
2
  • \$\begingroup\$ Also, for efficiency, replace your if sets with case switch code. \$\endgroup\$
    – Passerby
    Oct 29 '14 at 0:22
  • \$\begingroup\$ I had tried PROGMEM but that didn't seem to help. Thanks so much for the explanation on why it wouldn't work and for the possible solutions. I really like the third option and am working on implementing this solution. Thanks again! \$\endgroup\$
    – Elmer
    Oct 30 '14 at 16:34
0
\$\begingroup\$

Off the top of my head, I don't know the specifics about the Uno (ATMega328p) and the ATtiny85, other than that the latter has a fraction of the memory and code space of the former.

The good thing is, you are using const. That keyword places your non-changing variable char arrays, into static fields. Instead of being put into ram, it is put into code space. Code space tends to be much bigger than ram/memory. Very effecient.

The bad thing is that many IF statements are not effecient. Depending on your statements, you could condense them down into Case/Switch statements instead. Or condense even further with other case/switch tricks.

Most likely, the difference in ram and code space is causing optimization errors. Some statements or loops might be getting optimized out, in order to make it fit on the microcontroller.

Is the reason you want an ATtiny cost, or physical size? Because a Bare Bones arduino on a blank ATMega328p is cheap, and you ca always get a smd sized version if size is a concern.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Unfortunately avr-gcc does not automatically put variables declared as const into flash; it is required to use either PROGMEM or __flash (C only) in order to do so. \$\endgroup\$ Oct 27 '14 at 18:51
  • \$\begingroup\$ Ah, and so that may be it. Used to msp 430, const is all that is needed \$\endgroup\$
    – Passerby
    Oct 27 '14 at 19:28
  • \$\begingroup\$ yes Igancio has it right - using AVR's flash memory is a dark art, and despite having SO MUCH of it usually un-used, it's hard to define, set, and read data with it. It's a shame, because most programs probably only use 1/3 of the flash memory, and tens of KB end up untouched. \$\endgroup\$
    – KyranF
    Oct 27 '14 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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