If you want to use a microcontroller with a smaller number of pins, consider using NeoPixels. You can drive any number of them with a single data line.
For the sketch itself, as always I would use a class for the components.
This sketch compiles. Don’t have a board with me, so I can’t really test it.
/**
A flasher flashes a pin on and off. It can be made active or inactive - in inactive state,
the pin stays off.
The length of time the pin is on/off for is decided in a subclass. getOnMs and getOffMs are
called once each time the led is turned on or off.
*/
class Flasher {
public:
const byte pin;
boolean active = false;
boolean on = false;
unsigned long startMs;
unsigned long durationMs;
Flasher(byte pin) : pin(pin) {}
virtual unsigned long getOnMs() = 0;
virtual unsigned long getOffMs() = 0;
void setup() {
pinMode(pin, OUTPUT);
}
void loop() {
if (!active) return;
if (millis() - startMs < durationMs) return;
on = !on;
digitalWrite(pin, on ? HIGH : LOW);
startMs = millis();
durationMs = on ? getOnMs() : getOffMs();
}
void activate() {
active = true;
on = true;
digitalWrite(pin, HIGH);
startMs = millis();
durationMs = getOnMs();
}
void deactivate() {
active = false;
on = false;
digitalWrite(pin, LOW);
}
};
/**
A flasher that is on for N seconds and off for N seconds
*/
class NSecondFlasher : public Flasher {
public:
const unsigned long nMs;
NSecondFlasher(byte pin, int nSec) : Flasher(pin), nMs(nSec * 1000L) {}
unsigned long getOnMs() {
return nMs;
}
unsigned long getOffMs() {
return nMs;
}
};
/**
A flasher that is on for one second and off for a time picked randomly from a list
*/
class RandomFlasher: public Flasher {
public:
unsigned long *times;
int nTimes;
RandomFlasher(byte pin, unsigned long *times, int nTimes) :
Flasher(pin), times(times), nTimes(nTimes) {}
unsigned long getOnMs() {
return 1000;
}
unsigned long getOffMs() {
return times[random(nTimes)];
}
};
/**
A button listens to a pin and calls onRising and onFalling when the
pin goes high and low, with a 20ms debounce. Subclasses should override these methods -
the default implementations do nothing.
*/
class Button {
public:
static const unsigned long debounceMs = 20;
const byte pin;
int state;
unsigned long stateChangeMs;
Button(byte pin) : pin(pin) {}
void setup() {
pinMode(pin, INPUT_PULLUP);
state = digitalRead(pin);
}
void loop() {
if (millis() - stateChangeMs < debounceMs) return;
int prevState = state;
state = digitalRead(pin);
if (state == prevState) return;
stateChangeMs = millis();
if (state == LOW)
onFalling();
else
onRising();
}
virtual void onRising() {};
virtual void onFalling() {};
};
/**
a three flasher controller hold pointers to three flashers. As its button is pressed (ie: onFalling),
it acivates one, twoo, three flashers and then back to one again.
*/
class ThreeFlasherControllerButton: public Button {
public :
Flasher *flashers[3];
int nFlashers;
ThreeFlasherControllerButton(byte pin, Flasher &f1, Flasher &f2, Flasher &f3) :
Button(pin) {
flashers[0] = &f1;
flashers[1] = &f2;
flashers[2] = &f3;
}
void setup() {
Button::setup();
nFlashers = 1;
flashers[0]->activate();
flashers[1]->deactivate();
flashers[2]->deactivate();
}
// no implementation of loop() - the superclass does the stuff we need
void onFalling() {
if (nFlashers < 3) {
flashers[nFlashers]->activate();
nFlashers ++;
}
else {
while (nFlashers > 1) {
nFlashers --;
flashers[nFlashers]->deactivate();
}
}
}
};
// PINOUT
// flashers on pins 6, 7, and 9. Controller button on 4.
unsigned long led1Times[] = {3000, 5000, 8000};
unsigned long led2Times[] = {6000, 9000};
RandomFlasher led1(6, led1Times, sizeof(led1Times) / sizeof(*led1Times));
RandomFlasher led2(7, led2Times, sizeof(led2Times) / sizeof(*led2Times));
NSecondFlasher led3(9, 5);
ThreeFlasherControllerButton controller(4, led1, led2, led3);
void setup() {
led1.setup();
led2.setup();
led3.setup();
controller.setup();
}
void loop() {
led1.loop();
led2.loop();
led3.loop();
controller.loop();
}
This code is hereby released into the public domain. However, if it works for you and you use it (either as-is or modified) then the right thing to do would be to kick twenty bucks into my paypal pmurray@bigpond.com please.
If you need a bunch of stuff done to this sketch that is going to take time and back-and-forth to work out what you want and how to do it, well - I usually ask a hundred bucks for something that doesn’t need a lot of hardware setup and will take no more than a couple of hours.