How should I store a group of constant data of the same type?

I've analyzed the signal of a remote consisting of 4 buttons, each can be on or off. All buttons produce a signal that starts with a common 12 bits of data. Following that is 8 bits that identifies which button was pressed. Lastly, there are 4 bits which can be a command for either on or off. The signal ends with a 0 that seems a bit out of place, which I'm pretty sure is a stop bit, making 25 bits in total. (It seems out of place because the 'on' code: 0011, is the inverse of the 'off' code: 1100. With the 0 they would no longer be inverses of each other.) I'm using this to reproduce these signals to replace the remote.

For now I'm just storing these as global constants:

const bool COM[12] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1};
const bool OUT1[8] = {0, 1, 0, 1, 0, 0, 1, 1};
const bool OUT2[8] = {0, 1, 0, 1, 1, 1, 0, 0};
const bool OUT3[8] = {0, 1, 1, 1, 0, 0, 0, 0};
const bool OUT4[8] = {1, 1, 0, 1, 0, 0, 0, 0};
const bool ON[5] = {0, 0, 1, 1, 0};                // I've added the last 0 bit because why store it separate
const bool OFF[5] = {1, 1, 0, 0, 0};

It works, but I've learned in my CS courses that the use of global constants is generally bad practice. I need to keep my code neat because I have a lot of features to add. I'm also aware that the 8 bool array could be a byte. I'm not using the byte data type because making it a different data type from the rest would mead writing multiple functions for transmitting the data. Is there anything better than a bool array to store these values? If I could store them as bytes of varying sizes that would allow me to do bitwise operations, like replacing OFF with ~ON.

As for encapsulating this data, I'm not sure how to do that. It seems too small to bother saving in a .txt file on an SD card, but that is doable. I will have an SD card connected to the arduino for other parts of the project. Eventually I plan to write it all into its own library, and requiring an SD card to use it wouldn't be very practical.

You could save memory by storing the data as an array of 7 ints using bits of the ints as your "boolean" values. You could store the array in EEPROM or SD card but unless the program itself needs to change the values, why bother ? As the variables are declared as const I presume that this is not a requirement.

UKHeliBob:
You could save memory by storing the data as an array of 7 ints using bits of the ints as your "boolean" values.

To transmit the data I'm simply iterating through the array. If I iterate through an int, won't I have loads of unnecessary zeros? It may also be important to mention that the data is a bit weird. The way I've encoded it, the 1s are HIGH for 516 microseconds, then LOW for 168 microseconds. 0s are HIGH for 168 and LOW for 516. And there's no clock signal. So 0 is not the same as no data. Here's what it looks like on the scope:

This is 0000 0001 0101 0111 0000 0011 0
The output above is from the IC I'm attempting to replace. Sending the 1s and 0s into this function replicates that pretty much exactly:

void fskWrite(uint8_t pin, bool data) {
  if (data) { // 1, long-short
    digitalWrite(pin, HIGH);
    delayMicroseconds(515);
    digitalWrite(pin, LOW);
    delayMicroseconds(167);
  } else {      // 0, short-long
    digitalWrite(pin, HIGH);
    delayMicroseconds(167);
    digitalWrite(pin, LOW);
    delayMicroseconds(515);
  }
}

*I called it fsk(frequency shift keying) but I'm not really sure that's what it is

UKHeliBob:
You could store the array in EEPROM or SD card but unless the program itself needs to change the values, why bother ? As the variables are declared as const I presume that this is not a requirement.

They will be constant. Different objects will be assigned to an OUTx[8]. The transmit function take the object as a parameter to use its number.

If I iterate through an int, won't I have loads of unnecessary zeros?

Only if you iterate through all 16 bits. You presumably only read the required number of booleans from the arrays at the moment so read the required number of bits from the int

UKHeliBob:
16 bits

If an int is 16 bits and a bool is 1 bit, how does it save memory to use an int array? Does it work by putting all 7 variables under one address? Would it be even better to use a two dimensional bool array to store all these values at 1 address and eliminate the extra bits? It is rather inconvenient that the pieces of the signal are different sizes, but by moving the two bits from the end of the common to the beginning of each OUTx and having 'on' follow 'off' on the same line I can fit it all into a 10*6 bool array. Only 60 bits there vs 112 for the int array.

How would I initialize a constant two dimensional array?
Nevermind, I think I figured it out:

const bool codes[6][10] = {{0, 0, 0, 0, 0, 0, 0, 1, 0, 1},  // common
                           {0, 1, 0, 1, 0, 1, 0, 0, 1, 1},  // 1
                           {0, 1, 0, 1, 0, 1, 1, 1, 0, 0},  // 2
                           {0, 1, 0, 1, 1, 1, 0, 0, 0, 0},  // 3
                           {0, 1, 1, 1, 0, 1, 0, 0, 0, 0},  // 4
                           {0, 0, 1, 1, 0, 1, 1, 0, 0, 0}}; // on = first 5, off = last 5

If an int is 16 bits and a bool is 1 bit, how does it save memory to use an int array?

In the first place a boolean is not 1 bit it is 8 bits

So

const bool COM[12] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1};
const bool OUT1[8] = {0, 1, 0, 1, 0, 0, 1, 1};
const bool OUT2[8] = {0, 1, 0, 1, 1, 1, 0, 0};
const bool OUT3[8] = {0, 1, 1, 1, 0, 0, 0, 0};
const bool OUT4[8] = {1, 1, 0, 1, 0, 0, 0, 0};
const bool ON[5] = {0, 0, 1, 1, 0};                // I've added the last 0 bit because why store it separate
const bool OFF[5] = {1, 1, 0, 0, 0};

uses 54 bytes

Whereas

const int theData[7];

uses 14 bytes

You do not need a 2 dim array
To access say the first bit of the third level you could use

boolean theBool = bitRead(theData[2, 0]);

CORRECTION : See post #6 for a correction to this line of code

You could also get the boolean value by bit masking if you are comfortable with that and you can, of course, iterate through the bits of an array level if you want/need to retrieve a set of values.

To take the idea further you could use C bitfields and structs. Plenty of examples online

UKHeliBob:
To access say the first bit of the third level you could use

boolean theBool = bitRead(theData[2, 0]);

The closing square bracket is placed wrong IMHO.

boolean theBool = bitRead(theData[2], 0);

The closing square bracket is placed wrong IMHO.

Your HO is quite right
I will add a correction note to the post but leave the code as it is to avoid your comment becoming irrelevant