// https://forum.arduino.cc/t/timer-function-for-staggered-switching/1076239
// https://wokwi.com/projects/353770726969195521
# include "Button.h"
# include <BitArray.h>
unsigned long now; // the only use of millis() is to set this time
# define SIZE 8
# define CHANNELS 4
# define NSX 4 // actiual number of buttons attached
Button buttons[NSX] = {2, 3, 4, 5};
unsigned char buttonVector[SIZE];
unsigned char relayState[SIZE]; // very closely equal to the output
BitArray presetsBits; // 32 bits of preset on/off
# define FRONT 1000 // ms of mutation before a relay can change state
# define BACK 3000 // ms after realy change that mutation is kept on
const byte muteLED = 7;
Button upChannel = 9;
Button downChannel = 8;
# include <Adafruit_NeoPixel.h>
# define LED_PIN A0
# define LED_COUNT (SIZE + CHANNELS)
Adafruit_NeoPixel statusLEDs(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
xprintf("mad leech button mute midi POD\n");
// gang of 8 (only NSX are wired yet)
setupButtons();
// a few more buttons
upChannel.begin();
downChannel.begin();
presetsBits.begin(1, SIZE * CHANNELS); // single bits 0..31 as 4x8
presetsBits.clear();
statusLEDs.begin();
pinMode(muteLED, OUTPUT);
}
// nice mute functions
bool muteIsOn;
unsigned long muteTimer;
unsigned long muteWentOn;
void muteOn()
{
muteTimer = now; // refresh the lockout timer
if (muteIsOn == false) {
muteIsOn = true;
muteWentOn = now;
digitalWrite(muteLED, HIGH); // do relay or whatever needs doing
}
}
void muteCheck()
{
if (now - muteTimer < BACK) // time to unmute?
return;
if (muteIsOn == true) {
muteIsOn = false;
digitalWrite(muteLED, LOW); // undo relay or whatever needs doing
}
}
unsigned char currentChannel;
void loop() {
now = millis();
channelButtonCheck();
fillButtonVector();
for (unsigned char ii = 0; ii < NSX; ii++) {
if (buttonVector[ii]) {
unsigned char bitNumber = chanSlotToBit(currentChannel, ii);
presetsBits.set(bitNumber, !presetsBits.get(bitNumber));
}
}
// adjust relay states
relayFSM();
// time to unmute?
muteCheck();
// copy relayState to the hardware dirvers and lamps. muting has been handled.
for (unsigned char tt = 0; tt < NSX; tt++)
if (relayState[tt] == presetsBits.get(chanSlotToBit(currentChannel, tt)))
statusLEDs.setPixelColor(tt, relayState[tt] ? 0x0000ff : 0xff0000);
else
statusLEDs.setPixelColor(tt, 0xffff00);
statusLEDs.show(); // light 'em up
static unsigned long lastReport;
if (now - lastReport > 777) {
report();
lastReport = now;
}
}
void channelButtonCheck()
{
upChannel.read();
downChannel.read();
if (upChannel.uolPress()) {
currentChannel++;
if (currentChannel >= CHANNELS)
currentChannel = CHANNELS - 1;
}
if (downChannel.uolPress()) {
if (currentChannel)
currentChannel--;
}
// channel indicators are the n CHANNELS after the m SIZE relay LEDs
for (unsigned char tt = 0; tt < CHANNELS; tt++)
statusLEDs.setPixelColor(tt + SIZE, (tt == currentChannel) ? 0xff0000 : 0x006666);
}
// recall Button buttons[NSX] = {2, 3, 4, 5};
void setupButtons()
{
for (unsigned char ii = 0; ii < NSX; ii++)
buttons[ii].begin();
}
// however you detect a button going down
void fillButtonVector()
{
for (unsigned char ii = 0; ii < NSX; ii++)
buttons[ii].read();
for (unsigned char ii = 0; ii < NSX; ii++)
buttonVector[ii] = buttons[ii].uolPress();
}
// update the relayState, start in necessary and respect muting
void relayFSM()
{
for (unsigned char tt = 0; tt < SIZE; tt++) {
// if the relay (still) disagrees with the desired state, poke it along somehow
unsigned char bitNumber = chanSlotToBit(currentChannel, tt);
if (relayState[tt] != presetsBits.get(bitNumber)) {
if (muteIsOn) {
if (now - muteWentOn > FRONT) {
relayState[tt] = presetsBits.get(bitNumber);
muteOn(); // yes, it was. this extends it
}
}
muteOn(); // maybe turn it on, maybe extend it. either way...
}
}
}
// 8 bits per slot, so
unsigned char chanSlotToBit(unsigned char theChannel, unsigned char theSlot)
{
return (theChannel << 3) + theSlot;
}
void report()
{
if (1) return; // we may need a stinkin' report at some point
for (unsigned char tt = 0; tt < SIZE; tt++)
xprintf("%1d ", tt);
xprintf("\n");
for (unsigned char tt = 0; tt < SIZE; tt++)
xprintf("%s", relayState[tt] ? "rN " : "rF ");
xprintf("\n\n");
for (unsigned char cc = 0; cc < CHANNELS; cc++) {
for (unsigned char rr = 0; rr < SIZE; rr++)
xprintf("%s ", presetsBits.get(chanSlotToBit(cc, rr)) ? "X " : "_ ");
xprintf("\n");
}
xprintf("\n");
}
// programmer misses printf... THX @Danois90 https://forum.arduino.cc/t/pure-c-hello-world/1042838/21
void xprintf(const char *format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
Serial.print(buffer);
}
CH--
UNUSED
\ LEDS
CH++
PRESET
TOGGLE
RELAY