You can do what Majenko suggested, but all it takes is a diode per button to make the ORed line. I'm assuming the buttons are normally open and tied between ground and a pullup resistor. The ORed line is in addition to the individual button lines, which still need to be wired to processor pins. However, the advantage is that the ORed line will go low when any of the buttons are pressed. This can be used to wake the processor or cause a interrupt, which then has to look at the other lines to see which buttons are really pressed.
Another way is to not use button interrupts at all. Just scan the button lines every few ms. The rough rule of thumb is that 50 ms or less feels instantaneous to a human user. In other words, if you press a button and whatever it does is delayed by 50 ms, it still feels instantaneous to you.
If the point is to save power, you can actively drive the high side of the pullups. Keep them low most of the time so there will be no current even if a button is pressed. Turn on the pullups for maybe 10 µs before sampling the buttons, then turn them off again. This can be done in a short routine the processor wakes to every 10 ms, for example, when nothing is otherwise going on. The average power will be low since it will only be on for a few 10s of µs every few 10s of ms.
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)
Best Answer
There's INT0, and then there's PCINT[0:5]. INT0 gets its own interrupt routine, and the PCINT has one routine for all the pins configured to be used by the interrupt. That means that one of the first thing the interrupt routine needs to do is figure out which pin(s) actually changed to trigger the interrupt. Should actually be more convenient to use this way for your encoder, as you really want any encoder change to trigger the same interrupt routine.
I have no idea if all this is accessibile through the arduino platform, or if you need to program the controller yourself (as I'm not an Arduino user).