You have a square withe LEDs and an associated sensor. You can combine them in a struct or class; below uses a struct. A struct is like an entry in a phone book with some fields like a name, a phone number and possibly an address.
In you case, you have a field for the sensor pin and some form of field for the LEDs. I've called the struct PANEL and it has 4 fields as shown below.
struct PANEL
{
const uint8_t sensorPin; // the sensor pin
uint8_t sensorStatus; // status of the sensor
uint16_t firstLed; // first led of panel
uint16_t lastLed; // last led of panel
};
You can now create a two-dimensional array of panels (your matrix)
PANEL panels[2][4] = {
{
{12, !SENSORACTIVE, 0, 19},
{11, !SENSORACTIVE, 20, 39},
{10, !SENSORACTIVE, 40, 59},
{9, !SENSORACTIVE, 60, 79},
},
{
{8, !SENSORACTIVE, 80, 99},
{7, !SENSORACTIVE, 100, 119},
{5, !SENSORACTIVE, 120, 139},
{4, !SENSORACTIVE, 140, 159},
},
};
As I don't know how your sensor exactly works (it gives either HIGH or LOW), I've added a macro (at the top of the code) that is used in above
#define SENSORACTIVE HIGH
This assumes that if the sensor detects something it gives a HIGH; adjust to LOW if needed. One advantage is that it improves the readability of the code; anybody reading your code (including yourself) does not have to worry if HIGH after a digitalRead() of the sensor means activated or means not-activated.
One line in the two-dimensional array like {12, !SENSORACTIVE, 0, 19}
says that the first panel is controlled by the sensor on pin 12, has an initial status of not-activated and the LEDs associated with it are LEDs 0 to 19.
I've opted to have fields for the first LED and the last LED of a panel; the latter is not strictly necessary as your panels always have 20 LEDs and the last LED of a panel could actually be calculated.
To make it easier to work with arrays, I have defined a macro #define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
that calculates the number of elements in any type of array. The below part of setup() demonstrates how it can be used.
Serial.begin(115200);
Serial.print(F("Number of panel rows = "));
Serial.println(NUMELEMENTS(panels));
Serial.print(F("Number of panels in row = "));
Serial.println(NUMELEMENTS(panels[0]));
In setup() you can now iterate over the elements of the array to set the sensor pins to INPUT; change to INPUT_PULLUP if needed.
for (uint8_t yCnt = 0; yCnt < NUMELEMENTS(panels); yCnt++)
{
for (uint8_t xCnt = 0; xCnt < NUMELEMENTS(panels[yCnt]); xCnt++)
{
Serial.print(F("Setting sensor pin "));
Serial.print(panels[yCnt][xCnt].sensorPin);
Serial.println(F(" to input"));
pinMode(panels[yCnt][xCnt].sensorPin, INPUT);
}
}
In loop() I have declared two variables to count the rows (yCnt) and columns (xCnt). panels[yCnt][xCnt]
with e.g. yCnt being 1 and xCnt being 3 refers to the last panel in the last row.
void loop()
{
static uint8_t xCnt, yCnt;
Serial.print(F("Processing panels["));
Serial.print(yCnt);
Serial.print(F("]["));
Serial.print(xCnt);
Serial.println(F("]"));
...
...
Static variables are like global variables but they are only known inside the function that they are declared in. So their vaues will be remembered between successive calls to loop().
Next we read the sensor associated with the selected panel, compare it with the previous state (stored in the panel array) and take action if needed.
// read sensor
uint8_t sensorStatus = digitalRead(panels[yCnt][xCnt].sensorPin);
// state change
if (sensorStatus != panels[yCnt][xCnt].sensorStatus)
{
// remember state of sensor
panels[yCnt][xCnt].sensorStatus = sensorStatus;
if (panels[yCnt][xCnt].sensorStatus == SENSORACTIVE)
{
...
...
}
else
{
// panel off
...
...
}
// update physical strip
strip.show();
}
And in the last step we increment the column index (xCnt) and if necessary the row index (yCnt).
// next panel in row
xCnt++;
if (xCnt == NUMELEMENTS(panels[yCnt]))
{
xCnt = 0;
// next row
yCnt++;
}
if (yCnt == NUMELEMENTS(panels))
{
yCnt = 0;
}
Each time loop() is called, the sensor of the next panel will be checked (and action will be taken if needed).
The delay(1000) is only there to slow down the process so the first serial prints in loop() don't spam the serial monitor; that delay should not be there when you start expanding the sketch as it results in blocking code.
You can expand the struct definition with other fields if needed (e.g. the colour that should be set); you will also need adjust the PANEL array in that case.
The sketch is prepared to be non-blocking. If you need a responsive interaction, do not use for-loops / while-loops with delay() in them and do not use delay() in general; use millis() for any timing related work. Also don't use show() every time as it takes time; only use it when needed. Your problem will be that during a delay() or show() you can not read any sensors for other panels.