Last active
December 16, 2015 09:49
-
-
Save yrrah/5415948 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SD.h> | |
#include <MIDI.h> | |
#include <TimerOne.h> | |
#include <DmxSimple.h> | |
#include <TinyXML.h> | |
#include <string.h> | |
#define DEBUG 1 | |
#define USE_SERIAL_PORT Serial | |
#define USE_RUNNING_STATUS 0 | |
#define USE_1BYTE_PARSING 1 | |
#define USE_CALLBACKS 0 | |
#define MIDI_SYSEX_ARRAY_SIZE 64 | |
#define FRAME_GROUP 0 | |
#define LISTEN_CHAN 1 | |
#define CASE_VAL_L 2 | |
#define CASE_VAL_H 3 | |
#define MIDI_VAR 4 | |
#define MSG_TYPE 5 | |
#define FG_ADDR 1 | |
#define FG_LENGTH 0 | |
#define CURRENT_F 2 | |
#define PITCH 0 | |
#define VELOCITY 1 | |
#define POT1 41 | |
#define POT2 42 | |
#define POT3 43 | |
#define POT4 44 | |
#define FADER 45 | |
#define ledground1 8 //hole 2 | |
#define ledground2 5 //hole 4 | |
#define ledground3 24 //hole 13 | |
#define ledground4 16 //hole 11 | |
#define blueled 10 //hole 17 | |
#define greenled 11 //hole 18 | |
#define redled 12 // this goes to hole 19 on the ribbon cable | |
#define whitegreenled 13 //hole 15 | |
#define whiteblueled 14 | |
#define whiteredled 15 //hole 16 | |
#define switchground1 18 //hole 1 | |
#define switchground2 19 //hole 3 | |
#define switchground3 0 //hole 14 | |
#define switchground4 1 //hole 12 | |
#define switch1 26 //hole 7 | |
#define switch2 38 //hole 8 | |
#define switch3 39 //hole 9 | |
#define switch4 40 //hole 10 | |
File myFile; | |
volatile int milliSec = 0; | |
char* temp = ""; | |
int** caseArray; | |
int caseCount; | |
int** caseInfo; | |
int** fades; | |
int fgcount; //number of frame groups & corresponding cases | |
int maxChannel = 20; | |
double global_note, global_velocity; | |
volatile int period = 0; | |
int step = 1; | |
boolean XMLdone = 0; | |
int numScenes = 0; | |
char** fileNames; | |
int switches[4]={switch1,switch2,switch3,switch4}; | |
int switchgrounds[4]={switchground1,switchground2,switchground3,switchground4}; | |
int row; | |
int collumn; | |
int i=1; | |
int j=1; | |
int buttonpress; | |
void buttonled(int row,int collumn){ | |
switch (row){ | |
case 0: | |
digitalWrite(ledground1,LOW); | |
break; | |
case 1: | |
digitalWrite(ledground2,LOW); | |
break; | |
case 2: | |
digitalWrite(ledground3,LOW); | |
break; | |
case 3: | |
digitalWrite(ledground4,LOW); | |
break; | |
} | |
switch(collumn){ | |
case 0: | |
digitalWrite(redled,HIGH); | |
break; | |
case 1: | |
digitalWrite(greenled,HIGH); | |
break; | |
case 2: | |
digitalWrite(blueled,HIGH); | |
break; | |
case 3: | |
digitalWrite(whiteredled,HIGH); | |
digitalWrite(whitegreenled,HIGH); | |
digitalWrite(whiteblueled,HIGH); | |
break; | |
} | |
delay (50); | |
blackout(); | |
} | |
void ledtest(){ | |
for (int i=0; i<4; i++){ | |
for (int j=0; j<4; j++){ | |
buttonled(i,j); | |
delay (50); | |
blackout(); | |
delay(50); | |
} | |
} | |
} | |
void lightbutton(){ | |
for (int i=0; i<4; i++){ | |
for (int j=0; j<4; j++){ | |
digitalWrite(switchgrounds[i],LOW); | |
if (digitalRead(switches[j])==LOW){ | |
buttonpress=4*i+j; | |
buttonled(i,j); | |
Serial.println(buttonpress); | |
} | |
digitalWrite(switchgrounds[i],HIGH); | |
} | |
}} | |
void blackout(){ | |
digitalWrite(ledground1, HIGH); | |
digitalWrite(ledground2, HIGH); | |
digitalWrite(ledground3, HIGH); | |
digitalWrite(ledground4, HIGH); | |
digitalWrite(redled, LOW); | |
digitalWrite(greenled, LOW); | |
digitalWrite(blueled, LOW); | |
digitalWrite(whiteredled, LOW); | |
digitalWrite(whitegreenled, LOW); | |
digitalWrite(whiteblueled, LOW); | |
} | |
void XML_callback( uint8_t statusflags, char* tagName, uint16_t tagNameLen, char* data, uint16_t dataLen ) | |
{ | |
char ns[] = "num-scenes"; | |
char fn[] = "filename"; | |
int i = 0; | |
if (statusflags & STATUS_ATTR_TEXT) | |
{ | |
/* Serial.print("Attribute:"); | |
Serial.print(tagName); | |
Serial.print(" text:"); | |
Serial.println(data);*/ | |
if(!strcmp(ns, tagName)){ | |
numScenes = asciiHexToInt(data, 1); | |
Serial.print("numScenes: "); | |
Serial.println(numScenes); | |
fileNames = (char**) malloc(numScenes * sizeof (char*)); | |
} | |
if(!strcmp(fn, tagName)){ | |
Serial.print("Filename: "); | |
fileNames[i] = (char*) malloc((strlen(data)+1)*sizeof(char)); | |
strcpy(fileNames[i],data); | |
Serial.println(data); | |
if(numScenes == ++i){ | |
XMLdone = true; | |
} | |
} | |
} | |
} | |
static void readXML(){ | |
TinyXML xml; | |
uint8_t buffer[100]; | |
uint16_t buflen = 100; | |
xml.init((uint8_t*)&buffer,buflen,&XML_callback); | |
while(myFile.available()&&!XMLdone){ | |
char c = myFile.read(); | |
xml.processChar(c); | |
} | |
} | |
void loadFrame(int FrameID) { | |
int i; | |
int chan, val, upVal; | |
char in, in2; | |
#ifdef DEBUG | |
Serial.print("Load Frame Group:"); | |
Serial.print(FrameID); | |
Serial.print(" Frame:"); | |
Serial.println(caseInfo[FrameID][CURRENT_F]); | |
#endif | |
//seek to location of frame group on SD card | |
myFile.seek(caseInfo[FrameID][FG_ADDR]); | |
//find location of frame | |
i = 0; | |
while (i <= caseInfo[FrameID][CURRENT_F]) { | |
while (myFile.read() != '%'); | |
i++; | |
} | |
//read and execute DMX instructions | |
in = myFile.read(); | |
while (myFile.available() && in != '%' && in != '#') { | |
switch (in) { | |
case 'S': | |
case 'P': | |
case 'F': | |
case 'M': | |
in2 = myFile.read(); | |
myFile.readBytes(temp, 2); | |
chan = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
val = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
upVal = asciiHexToInt(temp, 2); | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("DMX Instruction: chan "); | |
Serial.print(chan, HEX); | |
Serial.print(", "); | |
Serial.print(val, HEX); | |
Serial.print(", "); | |
Serial.println(upVal, HEX); | |
#endif | |
double potValue; | |
int out; | |
switch (in) { | |
case 'S'://ST = Static | |
DmxSimple.write(chan, val); | |
break; | |
case 'P': | |
switch (in2) { | |
case '1'://P1 = Potentiometer 1 | |
potValue = analogRead(POT1); | |
break; | |
case '2'://P1 = Potentiometer 2 | |
potValue = analogRead(POT2); | |
break; | |
case '3'://P1 = Potentiometer 3 | |
potValue = analogRead(POT3); | |
break; | |
case '4'://P1 = Potentiometer 4 | |
potValue = analogRead(POT4); | |
break; | |
case '5'://P1 = Fader / Slider | |
potValue = analogRead(FADER); | |
break; | |
} | |
out = (int) ((potValue / 1023)*(upVal - val) + val); | |
DmxSimple.write(chan, out); | |
break; | |
case 'F': | |
switch (in2) { | |
case 'U'://FU = Fade Up | |
fades[chan][0] = 1; | |
DmxSimple.write(chan, val); | |
break; | |
case 'D'://FD = Fade Down | |
fades[chan][0] = 2; | |
DmxSimple.write(chan, upVal); | |
break; | |
} | |
fades[chan][1] = val; | |
fades[chan][2] = upVal; | |
break; | |
case 'M': | |
/* Serial.print(val); | |
Serial.print(" "); | |
Serial.println(upVal); | |
*/ switch (in2) { | |
case '1'://M1 = MIDI Note | |
out = (int) ((global_note / 127)*(upVal - val) + val); | |
break; | |
case '2'://M2 = MIDI Velocity | |
out = (int) ((global_velocity / 127)*(upVal - val) + val); | |
break; | |
} | |
// Serial.println(float2s(out, 5)); | |
DmxSimple.write(chan, out); | |
break; | |
} | |
break; | |
default: | |
#ifdef DEBUG | |
Serial.println("DMX Instuction Type not implemented yet"); | |
Serial.print(caseInfo[FrameID][CURRENT_F]); | |
Serial.print(" "); | |
Serial.print(FrameID); | |
Serial.print(" "); | |
Serial.print(in); | |
Serial.println(in2); | |
#endif | |
break; | |
} | |
in = myFile.read(); | |
} | |
//increment frame counter | |
if (++caseInfo[FrameID][CURRENT_F] >= caseInfo[FrameID][FG_LENGTH]) | |
caseInfo[FrameID][CURRENT_F] = 0; | |
} | |
void loadScene(char* fileName) { | |
int i; | |
int looplength; | |
// open the file. note that only one file can be open at a time, | |
// so you have to close this one before opening another. | |
myFile.close(); | |
myFile = SD.open(fileName, FILE_READ); | |
if (!myFile) { | |
// if the file didn't open, print an error: | |
#ifdef DEBUG | |
Serial.print("error opening scene: "); | |
Serial.println(fileName); | |
#endif | |
}else{ | |
//free the memory for the old case array if it exists | |
if (fgcount != 0) { | |
for (i = 0; i < fgcount; i++) | |
free(caseArray[i]); | |
free(caseArray); | |
} | |
myFile.readBytes(temp, 2); | |
fgcount = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
looplength = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
maxChannel = asciiHexToInt(temp, 2); | |
fades = (int**) malloc(maxChannel * sizeof (int*)); | |
for (i = 0; i < maxChannel; i++) { | |
fades[i] = (int*) malloc(3 * sizeof (int)); | |
fades[i][0] = 0; | |
} | |
#ifdef DEBUG | |
Serial.print("fgcount: "); | |
Serial.println(fgcount); | |
Serial.print("looplength: "); | |
Serial.println(looplength); | |
Serial.print("maxChannel: "); | |
Serial.println(maxChannel); | |
#endif | |
caseInfo = (int**) malloc(fgcount * sizeof (int*)); | |
for (i = 0; i < fgcount; i++) { | |
caseInfo[i] = (int*) malloc(3 * sizeof (int)); | |
while (myFile.read() != '#'); | |
myFile.readBytes(temp, 2); | |
caseInfo[i][FG_LENGTH] = asciiHexToInt(temp, 2); | |
caseInfo[i][FG_ADDR] = myFile.position(); | |
caseInfo[i][CURRENT_F] = 0; | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("------ Case Info "); | |
Serial.print(i); | |
Serial.println(" -------"); | |
Serial.print("FG Length: "); | |
Serial.println(caseInfo[i][FG_LENGTH]); | |
Serial.print("FG Address: "); | |
Serial.println(caseInfo[i][FG_ADDR]); | |
Serial.print("Current Frame: "); | |
Serial.println(caseInfo[i][CURRENT_F]); | |
#endif | |
} | |
while (myFile.read() != '$'); | |
myFile.readBytes(temp, 2); | |
caseCount = asciiHexToInt(temp, 2); | |
int fg = 0; | |
caseArray = (int**) malloc(caseCount * sizeof (int*)); | |
for (i = 0; i < caseCount; i++) { | |
caseArray[i] = (int*) malloc(6 * sizeof (int)); | |
char c = myFile.read(); | |
if(c == '$'){ | |
fg++; | |
} | |
caseArray[i][FRAME_GROUP] = fg; | |
//MIDI Listen Channel | |
myFile.readBytes(temp, 2); | |
caseArray[i][LISTEN_CHAN] = asciiHexToInt(temp, 2); | |
//MIDI low case value | |
myFile.readBytes(temp, 2); | |
caseArray[i][CASE_VAL_L] = asciiHexToInt(temp, 2); | |
//MIDI high case value | |
myFile.readBytes(temp, 2); | |
caseArray[i][CASE_VAL_H] = asciiHexToInt(temp, 2); | |
//MIDI varible (pitch,velocity,etc) | |
myFile.readBytes(temp, 1); | |
caseArray[i][MIDI_VAR] = asciiHexToInt(temp, 1); | |
//MIDI message type (note on, note off, etc) | |
myFile.readBytes(temp, 1); | |
caseArray[i][MSG_TYPE] = asciiHexToInt(temp, 1); | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("------ Case Array Index "); | |
Serial.print(i); | |
Serial.println(" -------"); | |
Serial.print("Frame Group "); | |
Serial.println(caseArray[i][FRAME_GROUP]); | |
Serial.print("MIDI Listen Channel "); | |
Serial.println(caseArray[i][LISTEN_CHAN]); | |
Serial.print("MIDI low case value "); | |
Serial.println(caseArray[i][CASE_VAL_L]); | |
Serial.print("MIDI high case value "); | |
Serial.println(caseArray[i][CASE_VAL_H]); | |
Serial.print("MIDI varible "); | |
Serial.println(caseArray[i][MIDI_VAR]); | |
Serial.print("MIDI message type "); | |
Serial.println(caseArray[i][MSG_TYPE]); | |
Serial.flush(); | |
#endif | |
} | |
} | |
} | |
void count() { | |
milliSec++; | |
// Serial.println(milliSec); | |
step = 255/period; | |
if(step==0)step = 1; | |
} | |
char * float2s(double f, unsigned int digits) { | |
int index = 0; | |
static char s[16]; // buffer to build string representation | |
// handle sign | |
if (f < 0.0) { | |
s[index++] = '-'; | |
f = -f; | |
} | |
// handle infinite values | |
if (isinf(f)) { | |
strcpy(&s[index], "INF"); | |
return s; | |
} | |
// handle Not a Number | |
if (isnan(f)) { | |
strcpy(&s[index], "NaN"); | |
return s; | |
} | |
// max digits | |
if (digits > 9) digits = 9; | |
long multiplier = pow(10, digits); // fix int => long | |
int exponent = int(log10(f)); | |
double g = f / pow(10, exponent); | |
if ((g < 1.0) && (g != 0.0)) { | |
g *= 10; | |
exponent--; | |
} | |
long whole = long(g); // single digit | |
long part = long((g - whole) * multiplier); // # digits | |
char format[16]; | |
sprintf(format, "%%ld.%%0%dld E%%+d", digits); | |
sprintf(&s[index], format, whole, part, exponent); | |
return s; | |
} | |
int asciiHexToInt(char* a, int length) { | |
//converts acii representation of hex eg 'ff' to 0xFF | |
int val; | |
int in; | |
int out = 0; | |
for (int i = length - 1; i >= 0; i--) { | |
in = a[i]; | |
if (in >= 'A' && in <= 'F') { | |
val = in - 55; | |
} else if (in >= 'a' && in <= 'f') { | |
val = in - 87; | |
} else if (in >= '0' & in <= '9') { | |
val = in - 48; | |
} else return -1; | |
out += val * (int) ceil((pow(16, length - 1 - i))); | |
} | |
return out; | |
} | |
boolean handleMIDI(int midiType, int midiChan, int note, int velocity) { | |
global_note = note; | |
global_velocity = velocity; | |
int midiValue = 0; | |
int i; | |
//MIDI Clock Signal | |
if (midiType == 1 && midiChan == 1 && note == 12/*C1*/ && velocity == 10) { | |
#ifdef DEBUG | |
Serial.print("Clock... Period: "); | |
Serial.println(period); | |
#endif | |
period = milliSec; | |
milliSec = 0; | |
} | |
#ifdef DEBUG | |
Serial.print("Type: "); | |
Serial.print(midiType); | |
Serial.print(" Chan: "); | |
Serial.print(midiChan); | |
Serial.print(" note: "); | |
Serial.print(note); | |
Serial.print(" velocity: "); | |
Serial.println(velocity); | |
#endif | |
//loop through all cases | |
for (int i = 0; i < caseCount; i++) { | |
//check to see if correct message type (Note on, Note off, etc) | |
if (caseArray[i][MSG_TYPE] == midiType | |
&& caseArray[i][LISTEN_CHAN] == midiChan) { | |
//get relevant incoming midi data | |
switch (caseArray[i][MIDI_VAR]) { | |
case PITCH: | |
midiValue = note; | |
break; | |
case VELOCITY: | |
midiValue = velocity; | |
break; | |
} | |
//do comparison and load frame if true | |
if (midiValue >= caseArray[i][CASE_VAL_L] | |
&& midiValue <= caseArray[i][CASE_VAL_H]) { | |
loadFrame(caseArray[i][FRAME_GROUP]); | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} | |
void loop() { | |
boolean dmx = 0; | |
int i; | |
// lightbutton(); | |
//faderpots(); | |
//ledtest(); | |
if (usbMIDI.read()) | |
dmx = handleMIDI(usbMIDI.getType(), usbMIDI.getChannel(), usbMIDI.getData1(), usbMIDI.getData2()); | |
else if (MIDI.read()) | |
handleMIDI(MIDI.getType(), MIDI.getChannel(), MIDI.getData1(), MIDI.getData2()); | |
if (step){ | |
for (i = 0; i < maxChannel; i++) { | |
if(fades[i][0] == 1){//fade up | |
fades[i][1] = fades[i][1] + step;//increase fade | |
if(fades[i][1]<fades[i][2]){//if val < max | |
DmxSimple.write(i, fades[i][1]); | |
Serial.print(i); | |
Serial.print(" fade up "); | |
Serial.print(period); | |
Serial.print(" x "); | |
Serial.println(fades[i][1]); | |
}else{ | |
fades[i][0] = 0;//disable fade | |
DmxSimple.write(i, 0); | |
} | |
}else if(fades[i][0] == 2){//fade down | |
fades[i][2] = fades[i][2] - step;//decrease fade | |
if(fades[i][2]>fades[i][1]){//if val > min | |
DmxSimple.write(i, fades[i][2]); | |
Serial.print(i); | |
Serial.print(" fade dn "); | |
Serial.print(period); | |
Serial.print(" x "); | |
Serial.println(fades[i][2]); | |
}else{ | |
fades[i][0] = 0;//disable fade | |
DmxSimple.write(i, 0); | |
} | |
}else{ | |
// DmxSimple.write(i, 0); | |
} | |
} | |
step=0; | |
} | |
} | |
void my_int1() { Serial.println("interrupt #1"); } | |
void my_int2() { Serial.println("interrupt #2"); } | |
void my_int3() { Serial.println("interrupt #3"); } | |
void my_int4() { Serial.println("interrupt #4"); } | |
void setup() { | |
/* pinMode(redled, OUTPUT); | |
pinMode(greenled, OUTPUT); | |
pinMode(blueled, OUTPUT); | |
pinMode(whiteredled, OUTPUT); | |
pinMode(whitegreenled, OUTPUT); | |
pinMode(whiteblueled, OUTPUT); | |
pinMode(ledground1, OUTPUT); | |
pinMode(ledground2, OUTPUT); | |
pinMode(ledground3, OUTPUT); | |
pinMode(ledground4, OUTPUT);*/ | |
pinMode(switchground1, INPUT); | |
pinMode(switchground2, INPUT); | |
pinMode(switchground3, INPUT); | |
pinMode(switchground4, INPUT); | |
// pinMode(switch1, OUTPUT); | |
// pinMode(switch2, OUTPUT); | |
// pinMode(switch3, OUTPUT); | |
// pinMode(switch4, OUTPUT); | |
// digitalWrite(switch1,HIGH); | |
// digitalWrite(switch2,HIGH); | |
// digitalWrite(switch3,HIGH); | |
// digitalWrite(switch4,HIGH); | |
attachInterrupt(7, my_int1, RISING); | |
attachInterrupt(6, my_int2, RISING); | |
attachInterrupt(1, my_int3, RISING); | |
attachInterrupt(0, my_int4, RISING); | |
DmxSimple.usePin(7); | |
DmxSimple.maxChannel(8); | |
//pinMode(3, OUTPUT); | |
MIDI.begin(MIDI_CHANNEL_OMNI); | |
Timer1.initialize(10000); //in microseconds (1000000us = 1s) | |
Timer1.attachInterrupt(count); //runs every millisecond | |
Timer1.start(); | |
#ifdef DEBUG | |
Serial.print("Initializing SD card..."); | |
#endif | |
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.*/ | |
// Note that even if it's not used as the CS pin, the hardware SS pin | |
// (10 on most Arduino boards, 53 on the Mega) must be left as an output | |
// or the SD library functions will not work. | |
pinMode(20, OUTPUT); | |
if (!SD.begin(20)) { | |
#ifdef DEBUG | |
Serial.println("initialization failed!"); | |
#endif | |
return; | |
} | |
myFile = SD.open("config.xml", FILE_READ); | |
if (!myFile) { | |
// if the file didn't open, print an error: | |
#ifdef DEBUG | |
Serial.println("error opening config file"); | |
#endif | |
}else{ | |
#ifdef DEBUG | |
Serial.println("initialization done."); | |
#endif | |
readXML(); | |
if(numScenes>0) | |
loadScene(fileNames[0]); | |
else | |
Serial.println("No scenes to load!"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment