Jump to content
43oh

MIDI Light controller


Recommended Posts

  • Replies 30
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Yes, those were made by Seeed. $24.90 for 10 boards 10cm x 10cm, 1.6mm, registered Air Parcel $8.10, total: $33.00 Shipping prices went up, shipping for 5cm x 5cm boards is now $4.10. Also, they d

After redesigning my board over and over, I have finally settled on the final version. It has 16 channels, uses 28pin 2553, has zero-crossing detector, MIDI, audio spectrum analyzer chip, and a confi

Here's my first light show. Sorry to disappoint you guys, but a) I do not own any Star Wars media, I am so busy at work and with the kids I hardly have any time for my hobbies. I know "It's No Good

Posted Images

Here's the code.

There is still room for improvement, like making zero crossing detector immune to noise, better handling of small loads like LED lights, and programmable MIDI channel and octave.

 

 

#include "msp430g2553.h"

#define MIDI_IN_PIN BIT1	// MIDI in pin
#define ZERO_X_PIN BIT0		// Zero Crossing Detector in pin
#define MIDI_RCVD_LED_PIN BIT2 // MIDI message received LED out pin
#define P1_OUT_PINS BIT4 + BIT5 + BIT6 + BIT7 // Outputs on P1

void setUp();

char charIndex = 0;			// general use index
char rxData = 0;			// received data
char rxByte = 1;			// data byte, first or second

char channel = 0;			// MIDI channel to listen to
char status = 0;			// Status byte converted to something easy to use
char runningStatus = 0;		// running status flag
char light = 255;			// light index
char lights[40];			// array of light brightness values (we are only using 10, but 40 or more are possible)

char counter = 1;			// PWM counter
char lightCounter = 0;		// used to track lights when setting/sending light on/off
int bitCounter = 0;			// used for setting parallel lights on/off

void main(void) {

   setUp();

   __bis_SR_register(GIE);				// enable global

   while(1) {							// main loop
       lightCounter = 0;				// clear light counter
       bitCounter = 1;					// first light on port 2 (P2.0-P2.5)
       while(lightCounter < 6) {		// loop through parallel lights 0-5
           if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... (unreliable when below 8)
               P2OUT |= bitCounter;	// turn light on
           }
           lightCounter++;				// next light
           P2OUT &= ~bitCounter;		// clear output
           bitCounter <<= 1;			// next bit on port 2
       }

       bitCounter = 0x10;				// first light on port 1 (P1.4-P1.7)
       while(lightCounter < 10) {		// loop through parallel lights 6-9
           if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... (unreliable when below 8)
               P1OUT |= bitCounter;	// turn light on
           }
           lightCounter++;				// next light
           P1OUT &= ~bitCounter;		// clear output
           bitCounter <<= 1;			// next bit on port 1
       }

       __bis_SR_register(LPM0_bits);	// go to sleep and wait for timer
   }

}

// increment count up to 64
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0 (void) {
   if(counter > 1) {
       counter--;							// decrement PWM counter
   }
   __bic_SR_register_on_exit(LPM0_bits);	// wake up and update lights
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void) {

   counter = 64;							// reset counter
   P1OUT &= ~(P1_OUT_PINS);				// clear outputs just in case
   P2OUT = 0;
   P1OUT |= MIDI_RCVD_LED_PIN;				// MIDI LED off
   P1IFG = 0;
}

void setUp() {

   WDTCTL = WDTPW + WDTHOLD;			// disable WDT
   BCSCTL1 = CALBC1_8MHZ;				// 8MHz clock
   DCOCTL = CALDCO_8MHZ;

   P1OUT |= MIDI_RCVD_LED_PIN;
   P1DIR |= MIDI_RCVD_LED_PIN;

   P1OUT &= ~(P1_OUT_PINS);			// port 1, 4 outs
   P1DIR |= (P1_OUT_PINS);

   P1SEL = MIDI_IN_PIN;				// setup MIDI in pin
   P1SEL2 = MIDI_IN_PIN;

   P2SEL &= ~(BIT6|BIT7);				// port 2 all out
   P2OUT = 0;
   P2DIR = 0xFF;

   P1DIR &= ~ZERO_X_PIN;				// port P1.0 zero crossing detector input
   P1IES |= ZERO_X_PIN;				// high/lo edge
   P1IFG &= ~ZERO_X_PIN;
   P1IE |= ZERO_X_PIN;

   CCTL0 = CCIE;						// setup timer
   CCR0 = 1042;						// 130.2us
   TACTL = TASSEL_2 + MC_1;			// SMCLK, upmode

   UCA0CTL1 |= UCSSEL_2;				// setup UART
   UCA0BR0 = 0x00;
   UCA0BR1 = 0x01;						// 31.25 Kbaud
   UCA0CTL1 &= ~UCSWRST;
   IE2 |= UCA0RXIE;

   charIndex = 0;						// clear lights
   while(charIndex < 40) {
       lights[charIndex] = 0;
       charIndex++;
   }
}

// this is where the MIDI magic happens
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {
   rxData = UCA0RXBUF;							// copy RX buffer to var
   if(rxData & BIT7) {							// is it status?
       if((rxData & 0xF8) != 0xF8) {			// ignore RealTime Category
           if((rxData & 0x0F) == channel) {	// are we listening on this channel
               status = (rxData >> 4) - 8;		// yep, let's set status
               runningStatus = 1;				// expect running status messages for our channel
           } else {							// nope, not our channel
               runningStatus = 0;				// do not process data for other channels
           }
       }
   } else {									// it's data byte
       if(runningStatus) {						// process data only for our channel including running status messages
           if(rxByte == 1) {					// is it first data byte?
               rxByte = 2;						// second byte will follow unless...
               if(status == 4 || status == 5) {// it's Prog Change or Channel Pressure
                   rxByte = 1;					// only one data byte expected
               } else if (status == 0 || status == 1) {// it's Note Off or Note On, select light
                   if(rxData > 59 && rxData < 100) {	// check range: 60-99, middle C to ...
                       light = rxData - 60;			// offset to start at 0
                       P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
                   }
               } else if (status == 3) {				// it's Controller
                   if(rxData > 0 && rxData < 17) {// check range: 1-16, General Purpose Slider
                       light = rxData - 1;		// offset to start at 0
                       P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
                   } else if(rxData == 120 || rxData == 123) {// if it's All Sound Off or All Notes Off...
                       charIndex = 0;
                       while(charIndex < 40) {	// clear all lights
                           lights[charIndex] = 0;
                           charIndex++;
                       }
                   }
               }
           } else if(rxByte == 2) {			// it's second data byte
               rxByte = 1;						// next byte will be first byte
               if(light == 255 )				// light not in range...
                   return;						// and return
               if(status == 1) {				// it's Note On
                   lights[light] = rxData >> 1;// velocity is brightness (divided by 2, 0-64 range)
               } else if(status == 0) {		// it's Note Off
                   lights[light] = 0;			// we ignore velocity and just clear the light
               } else if(status == 3) {		// it's Controller
                   if(light < 16) {				// are those sliders?
                       lights[light] = rxData >> 1;	// yes, we use sliders to control lights 0-15 (divided by 2, 0-64 range)
                   }
               }
               light = 255;					// set light to not in range
           }
       }
   }
}

 

post-197-135135502661_thumb.png

Link to post
Share on other sites

New code, this one has more robust and cleaner MIDI.

It is much easier to customize it now. For example handling notes and controllers on different channels is a breeze.

 

In the next version, I am thinking of adding some servo outputs so I can control direction of the light with controllers.

 

#include "msp430g2553.h"

#define MIDI_IN_PIN BIT1	// MIDI in pin
#define ZERO_X_PIN BIT0		// Zero Crossing Detector in pin
#define MIDI_RCVD_LED_PIN BIT2 // MIDI message received LED out pin
#define P1_OUT_PINS BIT4 + BIT5 + BIT6 + BIT7 // Outputs on P1

void setUp();
void handleMIDI(unsigned char rxByte);
void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity);
void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity);
void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value);

char charIndex = 0;			// general use index

unsigned char status = 0;	// MIDI status
unsigned char channel = 0;	// MIDI channel of the current MIDI status
unsigned char byteNumber = 1; // received byte number
unsigned char byteOne = 0;	// first byte received when status requires two bytes

unsigned char myChannel = 1; // channel we are listening on

char light = 255;			// light index
char lights[40];			// array of light brightness values

char counter = 1;			// PWM counter
char lightCounter = 0;		// used to track lights when setting/sending light on/off
int bitCounter = 0;			// used for setting parallel lights on/off

void main(void) {

   setUp();

   __bis_SR_register(GIE);				// enable global

   while(1) {							// main loop
       lightCounter = 0;				// clear light counter
       bitCounter = 1;					// first light on port 2 (P2.0-P2.5)
       while(lightCounter < 6) {		// loop through parallel lights 0-5
           if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter...
               P2OUT |= bitCounter;	// turn light on
           }
           lightCounter++;				// next light
           P2OUT &= ~bitCounter;		// clear output
           bitCounter <<= 1;			// next bit on port 2
       }

       bitCounter = 0x10;				// first light on port 1 (P1.4-P1.7)
       while(lightCounter < 10) {		// loop through parallel lights 6-9
           if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter...
               P1OUT |= bitCounter;	// turn light on
           }
           lightCounter++;				// next light
           P1OUT &= ~bitCounter;		// clear output
           bitCounter <<= 1;			// next bit on port 1
       }

       __bis_SR_register(LPM0_bits);	// go to sleep and wait for timer
   }

}

// increment count up to 64
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0 (void) {
   if(counter > 1) {
       counter--;							// decrement PWM counter
   }
   __bic_SR_register_on_exit(LPM0_bits);	// wake up and update lights
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void) {

   counter = 64;							// reset counter
   P1OUT &= ~(P1_OUT_PINS);				// clear outputs just in case
   P2OUT = 0;
   P1OUT |= MIDI_RCVD_LED_PIN;				// MIDI LED off
   P1IFG = 0;
}

void setUp() {

   WDTCTL = WDTPW + WDTHOLD;			// disable WDT
   BCSCTL1 = CALBC1_8MHZ;				// 8MHz clock
   DCOCTL = CALDCO_8MHZ;

   P1OUT |= MIDI_RCVD_LED_PIN;			// MIDI received (on our channel and status that we will be processing)
   P1DIR |= MIDI_RCVD_LED_PIN;

   P1OUT &= ~(P1_OUT_PINS);			// port 1, 4 outs
   P1DIR |= (P1_OUT_PINS);

   P1SEL = MIDI_IN_PIN;				// setup MIDI in pin
   P1SEL2 = MIDI_IN_PIN;

   P2SEL &= ~(BIT6|BIT7);				// port 2 all out
   P2OUT = 0;
   P2DIR = 0xFF;

   P1DIR &= ~ZERO_X_PIN;				// port P1.0 zero crossing detector input
   P1IES |= ZERO_X_PIN;				// high/lo edge
   P1IFG &= ~ZERO_X_PIN;
   P1IE |= ZERO_X_PIN;

   CCTL0 = CCIE;						// setup timer
   CCR0 = 1042;						// 130.2us
   TACTL = TASSEL_2 + MC_1;			// SMCLK, upmode

   UCA0CTL1 |= UCSSEL_2;				// setup UART
   UCA0BR0 = 0x00;
   UCA0BR1 = 0x01;						// 31.25 Kbaud
   UCA0CTL1 &= ~UCSWRST;
   IE2 |= UCA0RXIE;

   charIndex = 0;						// clear lights
   while(charIndex < 40) {
       lights[charIndex] = 0;
       charIndex++;
   }
}

// this is where the MIDI magic happens
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {
   handleMIDI(UCA0RXBUF); // handle received byte
}

void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity) {
   if(channel == myChannel) { // my channel?
       if(noteNumber > 59 && noteNumber < 100) { // check range: 60-99
           lights[noteNumber - 60] = velocity >> 1; // offset by 60, shift velocity to get 64 values only
           P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
       }
   }
}

void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity) {
   if(channel == myChannel) { // my channel?
       if(noteNumber > 59 && noteNumber < 100) {	// check range: 60-99
           lights[noteNumber - 60] = 0; // offset by 60, turn light off
           P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
       }
   }
}

void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value) {
   switch(controllerNumber) {
   case 123:
   case 120: // if it's All Sound Off or All Notes Off...
       P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
       charIndex = 0;
       while(charIndex < 40) {	// clear all lights
           lights[charIndex] = 0;
           charIndex++;
       }

       break;
   default:
       if(channel == myChannel) { // my channel?
           if(controllerNumber > 0 && controllerNumber < 17) { // check range: 1-16
               P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on
               lights[light = controllerNumber - 1] = value >> 1; // offset by 1, shift to get 64 values only
           }
       }
   }
}

void handleMIDI(unsigned char rxByte) {
   if(rxByte & BIT7) {					// is it status?
       if((rxByte & 0xF8) == 0xF8) {	// System RealTime category?
       	// skip, do nothing
       } else if ((rxByte & 0xF0) == 0xF0) { // System Common category?
           status = rxByte;			// get status
           switch(status) {
           case 0xF0: // skip, do nothing
           case 0xF7: // skip, do nothing
           case 0xF6: // skip, do nothing
               break;
           }
           byteNumber = 1;
       } else {						// Voice category
           channel = rxByte & 0x0F;	// get channel
           status = rxByte & 0xF0;		// get status
           byteNumber = 1;
       }
   } else {							// it's data byte
       if(byteNumber == 1) {			// is it first data byte?
           switch(status) {
           case 0xC0: // skip, do nothing
           case 0xD0: // skip, do nothing
           case 0xF0: // skip, do nothing
           case 0xF1: // skip, do nothing
           case 0xF3: // skip, do nothing
               break;
           default:
               byteNumber = 2;			// second byte will follow
               byteOne = rxByte;		// save first byte
           }
       } else {
           byteNumber = 1;				// next byte will be first byte
           switch(status) {
           case 0x90:					// note on
               if(byteOne) {			// handle only when >0, 0 is used as note off
                   handleNoteOn(channel, byteOne, rxByte);
                   break;
               }
           case 0x80:					// note off
               handleNoteOff(channel, byteOne, rxByte);
               break;
           case 0xB0:					// controller
               handleControlChange(channel, byteOne, rxByte);
               break;
           }
       }
   }
}

Link to post
Share on other sites
  • 3 months later...
  • 1 month later...

Yes, those were made by Seeed.

$24.90 for 10 boards 10cm x 10cm, 1.6mm, registered Air Parcel $8.10, total: $33.00

Shipping prices went up, shipping for 5cm x 5cm boards is now $4.10.

Also, they do not make 12 anymore, my last 3 orders all had 10 boards :(

 

BTW, do you know any fabs that can make custom flex strips?

 

Passed serial tests :)

Link to post
Share on other sites
  • 9 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...