/* LED Piano Key Learning Strip by Taylor Cone Code for MAX7219 originally by Nicholas Zambetti and Dave Mellis, modified by Marcus Hannerstig and Tomek Ness. Thanks to them for sharing their code! */ // Sets the number of LED driver chips (64 LEDs per chip, so we need 2 to cover 88 keys) int numMax7219 = 2; // Initialize Arduino output ports (the MAX7219's require 3 outputs total) int dataIn = 2; int load = 3; int clock = 4; // Initialize Arduino input ports (for us, just the potentiometer int sensorPin = A0; // Define max7219 registers byte max7219_reg_noop = 0x00; byte max7219_reg_digit0 = 0x01; byte max7219_reg_digit1 = 0x02; byte max7219_reg_digit2 = 0x03; byte max7219_reg_digit3 = 0x04; byte max7219_reg_digit4 = 0x05; byte max7219_reg_digit5 = 0x06; byte max7219_reg_digit6 = 0x07; byte max7219_reg_digit7 = 0x08; byte max7219_reg_decodeMode = 0x09; byte max7219_reg_intensity = 0x0a; byte max7219_reg_scanLimit = 0x0b; byte max7219_reg_shutdown = 0x0c; byte max7219_reg_displayTest = 0x0f; // BEGIN MAX7219 Functions // Puts a single byte of data to a MAX7219 void putByte(byte data) { byte i = 8; byte mask; while(i > 0) { mask = 0x01 << (i - 1); // get bitmask digitalWrite( clock, LOW); // tick if (data & mask) { // choose bit digitalWrite(dataIn, HIGH);// send 1 } else { digitalWrite(dataIn, LOW); // send 0 } digitalWrite(clock, HIGH); // tock --i; // move to lesser bit } } // Initializes all MAX7219's in the system void maxAll (byte reg, byte col) { int c = 0; digitalWrite(load, LOW); // begin for (c = 1; c <= numMax7219; c++) { putByte(reg); // specify register putByte(col); //((data & 0x01) * 256) + data >> 1); // put data } digitalWrite(load, LOW); digitalWrite(load,HIGH); } // Addresses individual MAX 7219's // Inputs: whichMax specifies which MAX7219 to change (1-2) // reg specifices register, col specifies column void maxOne(byte whichMax, byte reg, byte col) { int c = 0; digitalWrite(load, LOW); // begin for (c = numMax7219; c > whichMax; c--) { putByte(0); // no operation putByte(0); // no operation } putByte(reg); // specify register putByte(col);//((data & 0x01) * 256) + data >> 1); // put data for (c = whichMax-1; c >= 1; c--) { putByte(0); // no operation putByte(0); // no operation } digitalWrite(load,LOW); digitalWrite(load,HIGH); } // END MAX7219 Functions // BEGIN Custom Functions // Turns all LEDs off void allOff() { maxAll(1,0); maxAll(2,0); maxAll(3,0); maxAll(4,0); maxAll(5,0); maxAll(6,0); maxAll(7,0); maxAll(8,0); } // Turns all LEDs on void allOn() { maxAll(1,255); maxAll(2,255); maxAll(3,255); maxAll(4,255); maxAll(5,255); maxAll(6,255); maxAll(7,255); maxAll(8,255); } // Given a key input, returns the chip that key corresponds to (chip 1 = keys 1-64, chip 2 = keys 65-88) int getChip(int key) { if(key <= 64) { return 1; } else { return 2; } } // Given a key number input, returns appropriate row in matrix int getRow(int key) { int row = 0; if(getChip(key) == 1) { row = key / 8 + 1; if(key % 8 == 0) row--; } else { key = key - 64; row = key / 5 + 1; if(key % 5 == 0) row--; } return row; } // Given a key number input, returns appropriate column in matrix int getColumn(int key) { int col = 0; if(getChip(key) == 1) { col = key % 8; if(col == 0) col = 8; } else { key = key - 64; col = key % 5; if(col == 0) col = 5; } return col; } // Given a column number input, returns the column in binary form for MAX7219 int getBin(int col) { int bin = pow(2,col-1) + 1; // must add 1 because pow() is a float function, truncates ints if(bin == 2) bin = 1; if(bin == 3) bin = 2; return bin; } // Pauses execution for given number of beats void pause(int beats) { delay(analogRead(sensorPin)*3*beats); } // Sends appropriate data to chips to actually turn on LEDs void playKeys(int chip[], int row[], int bin[], int entries) { int chipBin1[8] = {0,0,0,0,0,0,0,0}; // each entry corresponds to a row on the first chip int chipBin2[5] = {0,0,0,0,0}; // same but for second chip for(int i = 1; i <= entries; i++) { if(chip[i-1] == 1) { chipBin1[row[i-1]-1] = chipBin1[row[i-1]-1] + bin[i-1]; } else { chipBin2[row[i-1]-1] = chipBin2[row[i-1]-1] + bin[i-1]; } } for(int k = 1; k <= 8; k++) { maxOne(1,k,chipBin1[k-1]); if(k <= 5) maxOne(2,k,chipBin2[k-1]); } } // Plays the given key void play1(int key, int beats) { maxOne(getChip(key), getRow(key), getBin(getColumn(key))); delay(analogRead(sensorPin)*3*beats); allOff(); } // Middleman function for 2 keys void play2(int key1, int key2, int beats) { int chip[2]; int row[2]; int bin[2]; chip[0] = getChip(key1); chip[1] = getChip(key2); row[0] = getRow(key1); row[1] = getRow(key2); bin[0] = getBin(getColumn(key1)); bin[1] = getBin(getColumn(key2)); playKeys(chip,row,bin,2); delay(analogRead(sensorPin)*3*beats); allOff(); } // Middleman function for 3 keys void play3(int key1, int key2, int key3, int beats) { int chip[3]; int row[3]; int bin[3]; chip[0] = getChip(key1); chip[1] = getChip(key2); chip[2] = getChip(key3); row[0] = getRow(key1); row[1] = getRow(key2); row[2] = getRow(key3); bin[0] = getBin(getColumn(key1)); bin[1] = getBin(getColumn(key2)); bin[2] = getBin(getColumn(key3)); playKeys(chip,row,bin,3); delay(analogRead(sensorPin)*3*beats); allOff(); } // Middleman function for 4 keys void play4(int key1, int key2, int key3, int key4, int beats) { int chip[4]; int row[4]; int bin[4]; chip[0] = getChip(key1); chip[1] = getChip(key2); chip[2] = getChip(key3); chip[3] = getChip(key4); row[0] = getRow(key1); row[1] = getRow(key2); row[2] = getRow(key3); row[3] = getRow(key4); bin[0] = getBin(getColumn(key1)); bin[1] = getBin(getColumn(key2)); bin[2] = getBin(getColumn(key3)); bin[3] = getBin(getColumn(key4)); playKeys(chip,row,bin,4); delay(analogRead(sensorPin)*3*beats); allOff(); } // Middleman function for 5 keys void play5(int key1, int key2, int key3, int key4, int key5, int beats) { int chip[5]; int row[5]; int bin[5]; chip[0] = getChip(key1); chip[1] = getChip(key2); chip[2] = getChip(key3); chip[3] = getChip(key4); chip[4] = getChip(key5); row[0] = getRow(key1); row[1] = getRow(key2); row[2] = getRow(key3); row[3] = getRow(key4); row[4] = getRow(key5); bin[0] = getBin(getColumn(key1)); bin[1] = getBin(getColumn(key2)); bin[2] = getBin(getColumn(key3)); bin[3] = getBin(getColumn(key4)); bin[4] = getBin(getColumn(key5)); playKeys(chip,row,bin,5); delay(analogRead(sensorPin)*3*beats); allOff(); } // Middleman function for 6 keys void play6(int key1, int key2, int key3, int key4, int key5, int key6, int beats) { int chip[6]; int row[6]; int bin[6]; chip[0] = getChip(key1); chip[1] = getChip(key2); chip[2] = getChip(key3); chip[3] = getChip(key4); chip[4] = getChip(key5); chip[5] = getChip(key6); row[0] = getRow(key1); row[1] = getRow(key2); row[2] = getRow(key3); row[3] = getRow(key4); row[4] = getRow(key5); row[5] = getRow(key6); bin[0] = getBin(getColumn(key1)); bin[1] = getBin(getColumn(key2)); bin[2] = getBin(getColumn(key3)); bin[3] = getBin(getColumn(key4)); bin[4] = getBin(getColumn(key5)); bin[5] = getBin(getColumn(key6)); playKeys(chip,row,bin,6); delay(analogRead(sensorPin)*3*beats); allOff(); } // Runs short visual initialization/demo light show void demo() { for(int i = 1; i <= 88; i++) { play1(i,.5); allOff(); } for(int i = 88; i >= 1; i--) { play1(i,.5); allOff(); } allOff(); delay(250); allOn(); delay(250); allOff(); delay(250); allOn(); delay(250); allOff(); delay(250); allOn(); delay(250); allOff(); } void FurElise() { for (int i = 1; i <= 2; i++) { // First intro play1(56,1); play1(55,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(48,1); play1(51,1); play2(25,52,1); play1(32,1); play1(37,1); play1(44,1); // Second intro play1(56,1); play1(55,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(52,1); play1(51,1); play2(25,49,1); play1(32,1); play1(37,1); // Third intro play1(56,1); play1(55,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(48,1); play1(51,1); play2(25,52,1); play1(32,1); play1(37,1); play1(44,1); // Fourth intro play1(56,1); play1(55,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(48,1); play1(51,1); play2(25,52,1); play1(32,1); play1(37,1); // Moving on play1(51,1); play1(52,1); play1(54,1); play2(28,56,1); play1(35,1); play2(40,56,1); play1(47,1); play1(57,1); play1(56,1); play2(35,54,1); play1(39,1); play2(35,54,1); play1(45,1); play1(56,1); play1(54,1); play2(25,52,1); play1(32,1); play2(37,52,1); play1(44,1); play1(54,1); play1(52,1); play2(32,51,1); play1(39,1); play1(44,1); play1(44,1); play1(56,1); play1(23,1); play1(35,1); play1(44,1); play1(56,1); play1(34,1); play1(35,1); play1(55,1); play1(56,1); play1(34,1); play1(35,1); play1(55,1); play1(56,1); play1(54,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play1(51,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(48,1); play1(51,1); play2(25,52,1); play1(32,1); play1(37,1); play1(44,1); play1(56,1); play1(55,1); play1(56,1); play1(55,1); play1(56,1); play1(51,1); play1(54,1); play1(52,1); play2(25,49,1); play1(32,1); play1(37,1); play1(40,1); play1(44,1); play1(49,1); play2(32,51,1); play1(36,1); play1(39,1); play1(44,1); play1(52,1); play1(51,1); play1(49,1); if(i == 1) { play2(25,49,1); play1(32,1); play1(37,1); play1(51,1); play1(52,1); play1(54,1); } else { play4(25,32,40,49,1); } } } void AxelFTheme() { play1(45,1); play1(48,1); play1(45,1); delay(50); play1(45,1); play1(50,1); play1(45,1); play1(43,1); play1(45,1); play1(52,1); play1(45,1); delay(50); play1(45,1); play1(53,1); play1(52,1); play1(48,1); play1(45,1); play1(52,1); play1(57,1); play1(45,1); play1(43,1); delay(50); play1(43,1); play1(40,1); play1(47,1); play1(45,1); } // Setup function void setup() { // Sets pins as inputs or outputs pinMode(dataIn, OUTPUT); pinMode(clock, OUTPUT); pinMode(load, OUTPUT); Serial.begin(9600); // Initialize MAX7219 maxAll(max7219_reg_scanLimit, 0x07); maxAll(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits) maxAll(max7219_reg_shutdown, 0x01); // not in shutdown mode maxAll(max7219_reg_displayTest, 0x00); // no display test for (int i = 1; i <= 8; i++) { // empty registers, turn all LEDs off maxAll(i,0); } maxAll(max7219_reg_intensity, 0x0f & 0x0f); // the first 0x0f is the value you can set (range: 0x00 to 0x0f) } // Loop function void loop() { demo(); delay(3000); FurElise(); delay(3000); }