#include "bhoreal.h" #include "Adafruit_NeoPixel.h" byte tempR; byte tempC; byte lastread; byte command = 0; boolean ready = true; boolean refresh_ok = false; uint16_t IntensityMAX = 255; // Default draw colour. Each channel can be between 0 and 4095. int red = 0; int green = 0; int blue = 0; // Auxiliary analog output definitions #define ANALOG0 A5 //POTENCIOMETRO #define ANALOG1 A1 boolean adc[2] = { //On or off state 0, 0}; byte analogval[2]; //The last reported value byte tempADC; //Temporary storage for comparison purposes uint16_t MODEL = MINISLIM; //Modelo uint16_t MAX = 4; int NUM_LEDS = 16; #define PIN 11 Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800); boolean pressed[4][4] = { {1,1,1,1}, {1,1,1,1}, {1,1,1,1}, {1,1,1,1} }; const byte remapMini[4][4] = { { 3, 4, 11, 12 }, { 2, 5, 10, 13 }, { 1, 6, 9, 14 }, { 0, 7, 8, 15 } }; int levelR[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; int levelG[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; int levelB[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; byte row[4] = { 13, 5, 10, 9}; byte column[4] = { 8, 6, 12, 4}; ////////////////////////////////////////////////////////////////////// ////////////////////// BHOREAL BEGIN ////////////////////// ////////////////////////////////////////////////////////////////////// void Bhoreal::begin(uint16_t DEVICE, uint32_t BAUD) { for(byte i = 0; i<4; i++) { pinMode(column[i], INPUT); pinMode(row[i], OUTPUT); digitalWrite(row[i], LOW); } // Start the serial port #if SERIAL_ENABLE Serial.begin(BAUD); #endif /* Setup the timer interrupt*/ strip.begin(); strip.show(); PORTE |= B01000000; DDRE |= B01000000; timer1Initialize(); //timer3Initialize(); // Disable Serial interrupt! } //////////////////////////////////////////////////////////////// ////////////////////// STARTUP ////////////////////// //////////////////////////////////////////////////////////////// // Run this animation once at startup. Currently unfinished. void Bhoreal::startup(){ for(int x = 0; x < NUM_LEDS; ++x){ uint32_t c = hue2rgb(x*8); uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; levelR[remapMini[x>>2][x%4]] = r; levelG[remapMini[x>>2][x%4]] = g; levelB[remapMini[x>>2][x%4]] = b; } for(int x = 0; x < NUM_LEDS; ++x) strip.setPixelColor(x, levelR[x], levelG[x], levelB[x]); strip.show(); } ////////////////////////////////////////////////////////////////////// ////////////////////// SERIAL PRESS & RELEASE ////////////////////// ////////////////////////////////////////////////////////////////////// void Bhoreal::on_press(byte r, byte c){ #if SERIAL_ENABLE Serial.write( 1); Serial.write( (r << 4) | c); #endif MIDIEvent e1 = { 0x09, 0x90, ((r << 2) | c) , 64 }; MIDIUSB.write(e1); } void Bhoreal::on_release(byte r, byte c){ #if SERIAL_ENABLE Serial.write( byte(0) ); Serial.write( (r << 4) | c); #endif MIDIEvent e1 = { 0x09, 0x90, ((r << 2) | c) , 0 }; MIDIUSB.write(e1); } /////////////////////////////////////////////////////////////// ////////////////////// CHECK BUTTONS ////////////////////// /////////////////////////////////////////////////////////////// void Bhoreal::checkButtons(){ for(byte c = 0; c < MAX; c++) { digitalWrite(row[c],HIGH); for(int r= MAX - 1; r >= 0; r--) { if(pressed[c][r] != digitalRead(column[r])) { // read the state delay(1); // Antirebotes!!! pressed[c][r] = digitalRead(column[r]); if(pressed[c][r]) on_press(c, r); else on_release(c, r); } } digitalWrite(row[c],LOW); } } //////////////////////////////////////////////////////////////// ////////////////////// REFRESH LED ////////////////////// //////////////////////////////////////////////////////////////// unsigned long time = 0; void Bhoreal::refresh(){ if (refresh_ok) { strip.show(); refresh_ok=false; } // if ((millis() - time)>=100) // { // strip.show(); // time = millis(); // } } //////////////////////////////////////////////////////////////// ////////////////////// REFRESH MIDI & LED ///////////////////// //////////////////////////////////////////////////////////////// void Bhoreal::midiRefresh(){ while(MIDIUSB.available() > 0) { MIDIEvent e; e = MIDIUSB.read(); #if SERIAL_ENABLE if(MIDI_DEBUG) { if(e.type != 0x0F) // timestamp 1 BYTE { Serial.print("Midi Packet: "); Serial.print(e.type); Serial.print("\t"); Serial.print(e.m1); Serial.print("\t"); Serial.print(e.m2); Serial.print("\t"); Serial.println(e.m3); } } #endif if((e.type == 0x09) && (e.m3)) // mensaje de NoteON con velocidad mayor que cero { uint32_t c = hue2rgb(e.m3); uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; strip.setPixelColor(remapMini[e.m2>>2][e.m2%4], r, g, b); strip.show(); } else if( (e.type == 0x08) || ((e.type == 0x09) && !e.m3) ) // mensaje de NoteOFF { strip.setPixelColor(remapMini[e.m2>>2][e.m2%4], 0, 0, 0); strip.show(); } MIDIUSB.flush(); // delete it??? } } //////////////////////////////////////////////////////////////// ////////////////////// CHECK ADC INPUTS ////////////////////// //////////////////////////////////////////////////////////////// void Bhoreal::checkADC(){ // For all of the ADC's which are activated, check if the analog value has changed, // and send a message if it has. if(adc[0]){ tempADC = (analogRead(ANALOG0) >> 2); if(abs((int)analogval[0] - (int)tempADC) > 3 ){ analogval[0] = tempADC; #if SERIAL_ENABLE Serial.write(14 << 4); Serial.write(analogval[0]); #endif } } if(adc[1]){ if(analogval[1] != (analogRead(ANALOG1) >> 2)){ analogval[1] = (analogRead(ANALOG1) >> 2); #if SERIAL_ENABLE Serial.write(14 << 4 | 1); Serial.write(analogval[1]); #endif } } } //////////////////////////////////////////////////////////////// ////////////////////// SERIAL INTERRUPT ////////////////////// //////////////////////////////////////////////////////////////// //boolean flag = true; /*TIMER*/ ISR(TIMER3_OVF_vect) { cli(); do{ // This do ensures that the data is always parsed at least once per cycle #if SERIAL_ENABLE if(Serial.available()){ if(ready){ // If the last command has finished executing, read in the next command and reset the command flag command = Serial.read(); ready = false; } switch (command >> 4) { //Execute the appropriate command, but only if we have received enough bytes to complete it. We might one day add "partial completion" for long command strings. case 1: // set colour if( Serial.available() > 2 ) { red = Serial.read(); green = Serial.read(); blue = Serial.read(); ready=true; } break; case 2: // led_on if( Serial.available() ) { lastread = Serial.read(); tempR = lastread >> 4; tempC = lastread & B1111; if ((tempR < MAX)&&(tempC < MAX)) { levelR[remapMini[tempC][tempR]] = red; levelG[remapMini[tempC][tempR]] = green; levelB[remapMini[tempC][tempR]] = blue; strip.setPixelColor(remapMini[tempC][tempR], red, green, blue); refresh_ok=true; } ready = true; } break; case 3: // led_off if( Serial.available() ) { lastread = Serial.read(); tempR = lastread >> 4; tempC = lastread & B1111; if ((tempR < MAX)&&(tempC < MAX)) { levelR[remapMini[tempC][tempR]] = 0; levelG[remapMini[tempC][tempR]] = 0; levelB[remapMini[tempC][tempR]] = 0; strip.setPixelColor(remapMini[tempC][tempR], 0, 0, 0); refresh_ok=true; } ready = true; } break; case 4: // led_row1 if( Serial.available() ) { tempR = command & B1111; lastread = Serial.read(); if (tempR < MAX) { for(tempC = 0; tempC < MAX; ++tempC){ if(lastread & (1 << tempC) ){ levelR[remapMini[tempR][tempC]] = red; levelG[remapMini[tempR][tempC]] = green; levelB[remapMini[tempR][tempC]] = blue; strip.setPixelColor(remapMini[tempR][tempC], red, green, blue); } else { levelR[remapMini[tempR][tempC]] = 0; levelG[remapMini[tempR][tempC]] = 0; levelB[remapMini[tempR][tempC]] = 0; strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0); } } } refresh_ok=true; ready = true; } break; case 5: // led_col1 if( Serial.available() ) { tempC = command & B1111; lastread = Serial.read(); if (tempC < MAX) { for(tempR = 0; tempR < MAX; ++tempR){ if(lastread & (1 << tempR) ){ levelR[remapMini[tempR][tempC]] = red; levelG[remapMini[tempR][tempC]] = green; levelB[remapMini[tempR][tempC]] = blue; strip.setPixelColor(remapMini[tempR][tempC], red, green, blue); } else { levelR[remapMini[tempR][tempC]] = 0; levelG[remapMini[tempR][tempC]] = 0; levelB[remapMini[tempR][tempC]] = 0; strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0); } } } refresh_ok=true; ready = true; } break; case 8: //frame if( Serial.available() > 7 ) { for(tempR=0; tempR < MAX; ++tempR){ lastread = Serial.read(); for(tempC = 0; tempC < MAX; ++tempC){ if(lastread & (1 << tempC) ){ levelR[remapMini[tempR][tempC]] = red; levelG[remapMini[tempR][tempC]] = green; levelB[remapMini[tempR][tempC]] = blue; strip.setPixelColor(remapMini[tempR][tempC], red, green, blue); } else { levelR[remapMini[tempR][tempC]] = 0; levelG[remapMini[tempR][tempC]] = 0; levelB[remapMini[tempR][tempC]] = 0; strip.setPixelColor(remapMini[tempR][tempC], 0, 0, 0); } } } refresh_ok=true; ready = true; } break; case 9: //clear if(command & 1){ byte TEMPMAX = MAX*MAX; for(int x = 0; x< TEMPMAX;++x){ levelR[x] = red; levelG[x] = green; levelB[x] = blue; strip.setPixelColor(x, red, green, blue); } } else{ byte TEMPMAX = MAX*MAX; for(int x = 0; x< TEMPMAX;++x){ levelR[x] = 0; levelG[x] = 0; levelB[x] = 0; strip.setPixelColor(x, 0, 0, 0); } } refresh_ok=true; ready = true; break; case 12: switch(command & 15){ case 0: adc[0] = true; analogval[0] = (analogRead(ANALOG0) >> 2); Serial.write(14 << 4); Serial.write(analogval[0]); break; case 1: adc[1] = true; analogval[1] = (analogRead(ANALOG1) >> 2); Serial.write(14 << 4 | 1); Serial.write(analogval[1]); break; default: break; } ready = true; break; case 13: adc[command & 15] = false; ready = true; break; default: break; } } #endif } // If the serial buffer is getting too close to full, keep executing the parsing until it falls below a given level // This might cause flicker, or even dropped messages, but it should prevent a crash. while (Serial.available() > TOOFULL); strip.show(); sei(); } /////////////////////////////////////////////////////////////// ////////////////////// TIMERS SETTINGS ////////////////////// /////////////////////////////////////////////////////////////// #define RESOLUTION 65536 // Timer1 is 16 bit unsigned int pwmPeriod; unsigned char clockSelectBits; char oldSREG; // To hold Status void setPeriodTimer1(long microseconds) // AR modified for atomic access { long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2 if(cycles < RESOLUTION) clockSelectBits = _BV(CS10); // no prescale, full xtal else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); // prescale by /8 else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64 else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); // prescale by /256 else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024 else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum oldSREG = SREG; cli(); // Disable interrupts for 16 bit register access ICR1 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode SREG = oldSREG; TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); TCCR1B |= clockSelectBits; // reset clock select register, and starts the clock } void Bhoreal::timer1Initialize() { TCCR1A = 0; // clear control register A TCCR1B = _BV(WGM13); // set mode 8: phase and frequency correct pwm, stop the timer setPeriodTimer1(5); TIMSK1 = _BV(TOIE1); } boolean flag = false; ISR(TIMER1_OVF_vect) { if (flag) { PORTE |= B01000000; flag=0;} else if (!flag) { PORTE &= B10111111; flag=1;} } void setPeriodTimer3(long microseconds) // AR modified for atomic access { long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2 if(cycles < RESOLUTION) clockSelectBits = _BV(CS30); // no prescale, full xtal else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS31); // prescale by /8 else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS31) | _BV(CS30); // prescale by /64 else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS32); // prescale by /256 else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS32) | _BV(CS30); // prescale by /1024 else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS32) | _BV(CS30); // request was out of bounds, set as maximum oldSREG = SREG; cli(); // Disable interrupts for 16 bit register access ICR3 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode SREG = oldSREG; TCCR3B &= ~(_BV(CS30) | _BV(CS31) | _BV(CS32)); TCCR3B |= clockSelectBits; // reset clock select register, and starts the clock } void Bhoreal::timer3Initialize() { TCCR3A = 0; // clear control register A TCCR3B = _BV(WGM33); // set mode 8: phase and frequency correct pwm, stop the timer setPeriodTimer3(10000); TIMSK3 = _BV(TOIE3); // TCCR3A = 0; // TCCR3B = 0< RGB ////////////////////// /////////////////////////////////////////////////////////////// uint32_t Bhoreal::hue2rgb(uint16_t hueValue) { uint8_t r; uint8_t g; uint8_t b; hueValue<<= 3; if (hueValue < 341) { // Lowest third of the potentiometer's range (0-340) hueValue = (hueValue * 3) / 4; // Normalize to 0-255 r = 255 - hueValue; // Red from full to off g = hueValue; // Green from off to full b = 1; // Blue off } else if (hueValue < 682) { // Middle third of potentiometer's range (341-681) hueValue = ( (hueValue-341) * 3) / 4; // Normalize to 0-255 r = 1; // Red off g = 255 - hueValue; // Green from full to off b = hueValue; // Blue from off to full } else { // Upper third of potentiometer"s range (682-1023) hueValue = ( (hueValue-683) * 3) / 4; // Normalize to 0-255 r = hueValue; // Red from off to full g = 1; // Green off b = 255 - hueValue; // Blue from full to off } return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; }