Just to show how not complicated it can be, here is a basic code (totally untested so might not work
) you could play with
#include <FastLED.h>
// For led chips like WS2812, which have a data line, ground, and power, you just need to define dataPin.
const uint8_t dataPin = 3;
// For led chipsets that are SPI based (four wires - data, clock, ground, and power),
// like the APA102, define both dataPin and clockPin
const uint8_t clockPin = 13; // 13 is SPI SCK on UNO, use 52 on a MEGA
// Define the array of leds
const uint8_t ledCount = 66; // How many you have
CRGB leds[ledCount];
// Define the zones on the map
const uint8_t maxLedPerZone = 10; // this is to make our life easy with fixed size arrays even if it wastes a bit of RAM
struct t_zone {
const uint8_t ledIDs[maxLedPerZone]; // where they are in the strip, starting at position 0
const uint8_t ledCount; // how many LEDs are in this area
const uint32_t lightingDuration; // How long the area stays lit in ms
const uint8_t buttonPin; // the pin of the button triggering this zone
bool ledsAreOn; // true if leds are on
uint32_t startTime; // utility for timeout
};
t_zone zones[] = {
/* leds position in strip count period button keep as is */
{{0, 1, 2, 3, 4, 5, 6} , 7 , 20000, 7, false, 0},
{{2, 3, 4, 7, 8, 9, 10} , 7 , 20000, 6, false, 0},
{{4, 5, 6, 10, 11, 12, 13, 60, 61} , 9 , 20000, 5, false, 0},
};
const uint8_t zoneCount = sizeof zones / sizeof zones[0];
bool ledIsOnInAnotherZone(uint8_t ledId, uint8_t currentZone)
{
bool ledIsOn = false;
for (uint8_t z = 0; z < zoneCount; z++) {
if ((z != currentZone) && zones[z].ledsAreOn) {
for (uint8_t i = 0; i < zones[z].ledCount; i++) {
if (ledId == zones[z].ledIDs[i]) {
ledIsOn = true;
break;
}
}
}
}
return ledIsOn;
}
void zoneOn(uint8_t zoneID) {
for (uint8_t i = 0; i < zones[zoneID].ledCount; i++)
leds[zones[zoneID].ledIDs[i]] = CRGB::White;
FastLED.show();
zones[zoneID].startTime = millis();
zones[zoneID].ledsAreOn = true;
}
void zoneOff(uint8_t zoneID) {
for (uint8_t i = 0; i < zones[zoneID].ledCount; i++)
if (!ledIsOnInAnotherZone(zones[zoneID].ledIDs[i], zoneID)) // only turn it off if it does not belong to another zone that is ON
leds[zones[zoneID].ledIDs[i]] = CRGB::Black;
FastLED.show();
zones[zoneID].ledsAreOn = false;
}
void checkButtons() {
for (uint8_t z = 0; z < zoneCount; z++)
if ((!zones[z].ledsAreOn) && (digitalRead(zones[z].buttonPin) == LOW))
zoneOn(z);
}
void checkTimeout() {
for (uint8_t z = 0; z < zoneCount; z++)
if (zones[z].ledsAreOn && (millis() - zones[z].startTime > zones[z].lightingDuration))
zoneOff(z);
}
void setup() {
// sanity check delay - allows reprogramming if accidently blowing power w/leds
delay(2000);
// declare button pins
for (uint8_t z = 0; z < zoneCount; z++) pinMode(zones[z].buttonPin, INPUT_PULLUP);
// Define led arrangement, see FastLED examples for strip definition
// https://github.com/FastLED/FastLED/blob/cfffa1351497425931fb9ef5bad0eeb7b0cf8645/examples/Blink/Blink.ino#L17-L57
FastLED.addLeds<APA102, dataPin, clockPin, RGB>(leds, ledCount); // BGR ordering is typical
}
void loop() {
checkButtons();
checkTimeout();
}
it's pretty short, less than 100 lines of code with some comments.
you need to adjust
- the led count:
const uint8_t ledCount = 66;
- the number of LEDs for the largest zone
const uint8_t maxLedPerZone = 10;
- the definition of the mapping between buttons and which LEDs are controlled
t_zone zones[] = {
/* leds position in strip count period button keep as is */
{{0, 1, 2, 3, 4, 5, 6} , 7 , 20000, 7, false, 0},
{{2, 3, 4, 7, 8, 9, 10} , 7 , 20000, 6, false, 0},
{{4, 5, 6, 10, 11, 12, 13, 60, 61} , 9 , 20000, 5, false, 0},
};
here I only defined 3 zones (and not using all the LEDs, just an example).
The first zone will impact leds at position 0, 1, 2, 3, 4, 5 and 6 which is the first array, then I say I've 7 active leds in the array, that this zone will stay on for 20s (20.000 ms) and is driven by a momentary push button on pin 7. (false and 0 and the end are for use at run time, just keep them there).
Last but not least, you need to define the right LED strip. have a look at the examples depending on which type of LED you bought.
Here I'm declaring an APA102
type.
FastLED.addLeds<APA102, dataPin, clockPin, RGB>(leds, ledCount);
dataPin, clockPin
are defined at the beginning of the code, clockPin
is not needed if you use Neopixels.
Wiring the momentary push buttons is straightforward: Pin --- Button -- GND
daisy chain the LEDs in the order that makes physical sense (keep line short) don't worry about having the LEDs in a given zone next to each other as you define their position in the array (and some might be in multiple zones).
power the led strip from both ends with a good 5V DC power supply (Meanwell is a good brand). It needs to be able to generate 60mA x ledCount (so a 4 Amps x 5 V power supply would be necessary for your 66 LEDs -> take a bit of margin for example a MeanWell RS-25-5 25W 5V 5A would do).