@marco_c @alto777
Here's the code. I've divided it into the function from the 'sender' (which has the midi shield and SD card), and the 'receiver' that is connected to the 3 Neopixel strips. I'll tidy up the 'sender' code when I have it working. I understand I only need the switch cases on the receiver Uno. All the sender needs to do is take the midi file from the SD card, play it, and send the events through i2c to the receiver.
My questions are:
- How can I activate the 3 neopixel 'notes' with a short delay between them before the note is played (physically heard) on the midi output? You can see I commented out the 50ms delay, as even this was messing up the tunes.
- You can see on the receiver code that I have added an if statement to the 'note on' case to deal with a note on with 0 velocity. Is there a better way of doing this?
- This is a general coding theory question regarding calling functions - I modified the receiver code from an i2c tutorial I found online. In the loop, I can't see the receiveEvent function being called. I only see it called in the setup. How is the code constantly receiving information from the i2c bus when it isn't continually being called in the main loop? (I told you I wans't a strong coder!)
OK, that's it. Thank you again for your continued support.
void playNote(uint8_t note, bool state)
{
if (note > 128) return;
}
void midiCallback(midi_event *pev){
Wire.beginTransmission(4); // transmit to device #4
Wire.write(pev->data[0]); //sending 0x12 to Slave
Wire.write(pev->data[1]); //sending 0x45 to Slave
Wire.write( pev->data[2]); //sending 0x12 to Slave
Wire.endTransmission();
if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
{
Serial.write(pev->data[0] | pev->channel);
Serial.write(&pev->data[1], pev->size-1);
}
else
Serial.write(pev->data, pev->size);
uint8_t EventID = pev->data[0];
uint8_t pitch = pev->data[1];
uint8_t velocity = pev->data[2];
switch (pev->data[0])
{
case NOTE_OFF: // [1]=note no, [2]=velocity
DEBUGS(" NOTE_OFF");
playNote(pev->data[1], SILENT);
break;
case NOTE_ON: // [1]=note_no, [2]=velocity
DEBUGS(" NOTE_ON");
// Note ON with velocity 0 is the same as off
playNote(pev->data[1], (pev->data[2] == 0) ? SILENT : ACTIVE);
break;
case POLY_KEY: // [1]=key no, [2]=pressure
DEBUGS(" POLY_KEY");
break;
case PROG_CHANGE: // [1]=program no
DEBUGS(" PROG_CHANGE");
break;
case CHAN_PRESS: // [1]=pressure value
DEBUGS(" CHAN_PRESS");
break;
case PITCH_BEND: // [1]=MSB, [2]=LSB
DEBUGS(" PITCH_BLEND");
break;
case CTL_CHANGE: // [1]=controller no, [2]=controller value
{
DEBUGS(" CTL_CHANGE");
switch (pev->data[1])
{
default: // non reserved controller
break;
case CH_RESET_ALL: // no data
DEBUGS(" CH_RESET_ALL");
break;
case CH_LOCAL_CTL: // data[2]=0 off, data[1]=127 on
DEBUGS(" CH_LOCAL_CTL");
break;
case CH_ALL_NOTE_OFF: // no data
DEBUGS(" CH_ALL_NOTE_OFF");
// for (uint8_t i = 0; i < ARRAY_SIZE(pinIO); i++)
// {
// uint8_t pin = pgm_read_byte(pinIO + i);
// digitalWrite(pin, SILENT);
// }
break;
case CH_OMNI_OFF: // no data
DEBUGS(" CH_OMNI_OFF");
break;
case CH_OMNI_ON: // no data
DEBUGS(" CH_OMNI_ON");
break;
case CH_MONO_ON: // data[2]=0 for all, otherwise actual qty
DEBUGS(" CH_MONO_ON");
break;
case CH_POLY_ON: // no data
DEBUGS(" CH_POLY_ON");
break;
}
}
break;
}
}
and the reveiver...
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
volatile byte myData[3];
volatile bool flag = false;
Adafruit_NeoPixel pixel1(101, 5, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixel2(101, 4, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixel3(101, 3, NEO_GRB + NEO_KHZ800);
// Define constants for MIDI channel voice message IDs
const uint8_t NOTE_OFF = 0x80; // note on
const uint8_t NOTE_ON = 0x90; // note off. NOTE_ON with velocity 0 is same as NOTE_OFF
const uint8_t POLY_KEY = 0xa0; // polyphonic key press
const uint8_t CTL_CHANGE = 0xb0; // control change
const uint8_t PROG_CHANGE = 0xc0; // program change
const uint8_t CHAN_PRESS = 0xd0; // channel pressure
const uint8_t PITCH_BEND = 0xe0; // pitch bend
// Define constants for MIDI channel control special channel numbers
const uint8_t CH_RESET_ALL = 0x79; // reset all controllers
const uint8_t CH_LOCAL_CTL = 0x7a; // local control
const uint8_t CH_ALL_NOTE_OFF = 0x7b; // all notes off
const uint8_t CH_OMNI_OFF = 0x7c; // omni mode off
const uint8_t CH_OMNI_ON = 0x7d; // omni mode on
const uint8_t CH_MONO_ON = 0x7e; // mono mode on (Poly off)
const uint8_t CH_POLY_ON = 0x7f; // poly mode on (Omni off)
const char fileName[] = "AIR.MID";
const uint8_t ACTIVE = HIGH;
const uint8_t SILENT = LOW;
#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels
void setup() {
Wire.begin(4); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
pixel1.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixel2.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixel3.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixel1.clear(); // Set all pixel colors to 'off'
pixel2.clear(); // Set all pixel colors to 'off'
pixel3.clear(); // Set all pixel colors to 'off'
pixel1.setBrightness(255);
pixel2.setBrightness(255);
pixel3.setBrightness(255);
pixel1.show(); // Send the updated pixel colors to the hardware.
pixel2.show(); // Send the updated pixel colors to the hardware.
pixel3.show(); // Send the updated pixel colors to the hardware.
}
void loop() {
if (flag == true)
{
Serial.println(myData[0], HEX); //shows: 12
Serial.println(myData[1], HEX); //shows: 45
Serial.println(myData[2], HEX); //shows: 45
flag = false;
}
uint8_t EventID = myData[0];
uint8_t pitch = myData[1];
uint8_t velocity = myData[2];
switch (myData[0])
{
case NOTE_OFF: // [1]=note no, [2]=velocity
pixel1.setPixelColor(127-pitch, pixel1.Color(0, 0, 0));
pixel2.setPixelColor(127-pitch, pixel2.Color(0, 0, 0));
pixel3.setPixelColor(127-pitch, pixel3.Color(0, 0, 0));
pixel1.show(); // Send the updated pixel colors to the hardware.
pixel2.show(); // Send the updated pixel colors to the hardware.
pixel3.show(); // Send the updated pixel colors to the hardware.
break;
case NOTE_ON: // [1]=note_no, [2]=velocity
pixel1.setPixelColor(127-pitch, pixel1.Color(150,0 , 0));
pixel2.setPixelColor(127-pitch, pixel2.Color(0, 0, 150));
pixel3.setPixelColor(127-pitch, pixel3.Color(0, 150, 0));
pixel1.show(); // Send the updated pixel colors to the hardware.
//delay(50);
pixel2.show(); // Send the updated pixel colors to the hardware.
//delay(50);
pixel3.show(); // Send the updated pixel colors to the hardware.
if (velocity == 0) //0x90 is note on
{
pixel1.setPixelColor(127-pitch, pixel1.Color(0, 0, 0));
pixel2.setPixelColor(127-pitch, pixel2.Color(0, 0, 0));
pixel3.setPixelColor(127-pitch, pixel3.Color(0, 0, 0));
pixel1.show(); // Send the updated pixel colors to the hardware.
pixel2.show(); // Send the updated pixel colors to the hardware.
pixel3.show(); // Send the updated pixel colors to the hardware.
}
break;
case POLY_KEY: // [1]=key no, [2]=pressure
break;
case PROG_CHANGE: // [1]=program no
break;
case CHAN_PRESS: // [1]=pressure value
break;
case PITCH_BEND: // [1]=MSB, [2]=LSB
break;
case CTL_CHANGE: // [1]=controller no, [2]=controller value
{
{
default: // non reserved controller
break;
case CH_RESET_ALL: // no data
break;
case CH_LOCAL_CTL: // data[2]=0 off, data[1]=127 on
break;
case CH_ALL_NOTE_OFF: // no data
break;
case CH_OMNI_OFF: // no data
break;
case CH_OMNI_ON: // no data
break;
case CH_MONO_ON: // data[2]=0 for all, otherwise actual qty
break;
case CH_POLY_ON: // no data
break;
}
}
break;
}
}
void receiveEvent(int howMany) //howMany = data bytes received from Amster = 2 here
{
for (int i = 0; i < howMany; i++)
{
myData[i] = Wire.read();
}
flag = true;
}```