#include <SoftwareSerial.h>;
// Button handling variables
int beatstate; // the current reading_beat from the input pin
int last_beatstate = LOW; // the previous reading_beat from the input pin
int switchstate;
int last_switchstate = LOW;
int debounce_time = 100; // don't expect two pushes any quicker than this
// Beat counting variables
unsigned long current_time; // system time at this button push, in ms
unsigned long prev_beattime = 0; // system time at last button push, in ms
unsigned long prev_switchtime = 0;
const int beats_max = 512; // just in case you push the button too many times
int beat_lengths[beats_max]; // array to store the beat lengths
int beat_length = 500; // calculated average of beat lengths
int beat_index = 0; // number of button presses
const int beat_timeout = 2000; // time after which beat_index resets, in ms
// Animation variables
int anim_time = 0; // time since last frame displayed
int frame_rate = 8; // how many frames per beat
const int max_pattern_length = 66; // includes two bytes for frame rate and length
int pattern_length = 16; // default loop point
int frame_length = 250; // how long a frame lasts in ms - 55ms is ~135bpm
int channel_count = 8; // maximum 8 connected wires
//byte frames[max_pattern_length]= {8,8, // first bytes are frame rate and pattern length. latter should be integer multiple of former or the animation desynch
//1,2,4,8,16,32,64,128,1,2,4,8,16,32,64,128}; // each remaining byte is a frame. Each bit of each byte is a channel.
int current_frame = 2; // index to animation array
unsigned long anim_start = 0; // system time at start of animation
int frame_threshold = 0; // when it decreases, we advance to the next frame
int frame_prev_threshold = 0;
// Pattern switching variables
int pattern_index=0; // index of list of animations
int pattern_count=9; // 0-indexed count of patterns
// Animations listed as frame rate, frame count, binary frame data
const byte pattern_matrix[][max_pattern_length] = {
{1,8, // Test pattern. each channel on the beat
1,2,4,8,16,32,64,128
},
{8,16, //Accelerating Zoom in out
B10000000,
B10000000,
B10000000,
B10000000,
B01000000,
B01000000,
B00100000,
B00001000,
B00000001,
B00000001,
B00000001,
B00000001,
B00000010,
B00000010,
B00000100,
B00010000,
},
{24,48, //Sparkle
1,32,8,2,2,128,16,128,1,1,128,32,128,2,128,64,16,8,64,64,4,128,32,32,32,2,32,2,16,32,2,4,8,16,1,16,32,8,4,16,64,4,2,16,16,1,16,8}
{7,14, //Zoom in and out every two beats
B10000000,
B01000000,
B00100000,
B00010000,
B00001000,
B00000100,
B00000010,
B00000001,
B00000010,
B00000100,
B00001000,
B00010000,
B00100000,
B01000000,
},
{8,8, // Zoom out once per beat
B00000001,
B00000010,
B00000100,
B00001000,
B00010000,
B00100000,
B01000000,
B10000000
},
{8,8, //Zoom in once per beat
B10000000,
B01000000,
B00100000,
B00010000,
B00001000,
B00000100,
B00000010,
B00000001
}
};
/*******************Setup Loop***************************/
void setup() {
// Start terminal
Serial.begin(115200); // Any baud rate should work
Serial.println("Started in slo-mo. Click Beat button to synch");
// Initialize elwire outputs
pinMode(2, OUTPUT); // channel A index 0
pinMode(3, OUTPUT); // channel B index 1
pinMode(4, OUTPUT); // channel C index 2
pinMode(5, OUTPUT); // channel D index 3
pinMode(6, OUTPUT); // channel E index 4
pinMode(7, OUTPUT); // channel F index 5
pinMode(8, OUTPUT); // channel G index 6
pinMode(9, OUTPUT); // channel H index 7
// Initialise button inputs
pinMode(A3, INPUT_PULLUP); // BPM button
pinMode(A2, INPUT_PULLUP); // Pattern switch button
// Pin 13 is the onboard status LED if we need it
pinMode(13, OUTPUT);
Serial.print("Pattern: ");
Serial.println(pattern_index);
Serial.print("Beat Length: ");
Serial.print(beat_length);
Serial.println("ms");
}
/*******************Main Loop***************************/
void loop() {
// read the current time and calculate time since last good button reading_beat
int current_time = millis();
unsigned long elapsed_switchtime = current_time - prev_switchtime;
unsigned long elapsed_beattime = current_time - prev_beattime;
// read the state of the buttons
int reading_beat = digitalRead(A3);
int reading_switch = digitalRead(A2);
// if it's the first beat push in a while, reset the indexes and timers
if (reading_beat == HIGH && last_beatstate == LOW && elapsed_beattime > beat_timeout){
beat_index = 0;
current_frame = 2;
anim_start = current_time;
prev_beattime = current_time;
frame_threshold = 0;
frame_prev_threshold = 100;
}
// if it's been long enough, but not too long, then record a new beat.
if (reading_beat == HIGH && last_beatstate == LOW && elapsed_beattime < beat_timeout && elapsed_beattime > debounce_time) {
// record this as the new previous time
prev_beattime = current_time;
// increment the beat index and if we reach the end, go back round, just in case
++beat_index;
if (beat_index == beats_max) beat_index = 0;
// set the current beat to the measured time
beat_lengths[beat_index] = elapsed_beattime;
// calculate the new average beat length and frame length
int running_total = 0;
for (int i=0; i<=beat_index; i++) {
running_total += beat_lengths[i];
}
beat_length = running_total / beat_index;
frame_length = beat_length / pattern_matrix[pattern_index][0];
// and set the button state to HIGH so we don't trigger this again too soon
last_beatstate = HIGH;
Serial.print("Beat Length: ");
Serial.print(beat_length);
Serial.println("ms");
}
else {
last_beatstate = LOW;
}
// if the pattern switch button is pressed, switch patterns
if (reading_switch == LOW && last_switchstate == HIGH && elapsed_switchtime > debounce_time) {
// record this as the new previous time
prev_switchtime = current_time;
// increment the pattern index and if we reach the end, go back round, ignoringn pattern 0 (the test pattern)
++pattern_index;
if (pattern_index == pattern_count) pattern_index = 1;
last_switchstate = HIGH;
Serial.print("Pattern: ");
Serial.println(pattern_index);
}
else {
last_switchstate = LOW;
}
// update animation timers and see if we've passed a frame threshold
anim_time = current_time - anim_start; // how long since the animation started
frame_threshold = anim_time % frame_length; // divide by frame length and find remainder
// If a frame has passed, increment the frame and set the outputs
if ( frame_threshold < frame_prev_threshold) { // when the remainder goes from high to low, we have started a new frame
if (current_frame >= pattern_matrix[pattern_index][1]+2) current_frame = 2;
int pinout=0;
int channel_state=LOW;
for (int i=0; i< channel_count; i++){
channel_state = bitRead(pattern_matrix[pattern_index][current_frame], i);
pinout = i+2;
digitalWrite(pinout,channel_state);
}
++current_frame;
}
frame_prev_threshold = frame_threshold;
// save the reading_beat. Next time through the loop, it'll be the lastButtonState:
last_beatstate = reading_beat;
last_switchstate = reading_switch;
}