Hello,
I'm using an Arduino UNO R3 to run a modified version of code that reads MIDI files from an SD card and sends it as a digital output as mentioned here. The code I have right now works all right, it plays the MIDI file and moves to the next one using an IR remote. But whenever it first starts to play, all the digital pins (which I have connected to an eight-channel relay) are set to LOW by default. That is to say that power is allowed to flow through all the relays. And since the actuators that I have connected to the NC of the relay are wired in parallel, they can only play one note at a time due to amp constraints, meaning that until every note is read at least once, no note can be played by the actuators. So, I just want to find a way to set all the pins to HIGH before the notes are read.
*I also want to specify that most of the code that I included was written by eijifrey, not me.
#include "SdFat.h"
#include <MD_MIDIFile.h>
#include <IRremote.h>
#define SERIAL_RATE 57600
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
const uint8_t NOTE_SIZE = 1; // Number of items in each note listing
// Define a structure to contain all the related data for executing a note
typedef struct
{
const int pin; // Motor output relay pin
const int note[NOTE_SIZE]; // MIDI pitch value for each "note"
} noteData_t;
noteData_t noteData[] =
{
{2, {84}}, // Note C5
{3, {86}}, // Note D5
{4, {88}}, // Note E5
{5, {89}}, // Note F5
{6, {91}}, // Note G5
{7, {93}}, // Note A5
{8, {95}}, // Note B5
{9, {96}} // Note C6
};
// SD chip select pin for SPI comms.
const uint8_t SD_SELECT = 10;
// The files in the tune list should be located on the SD card
// or an error will occur opening the file and the next in the
// list will be opened (skips errors).
const char *tuneList[] =
{
"6.mid", // Most complex and fast file
"7.mid", // Another example tune
"8.mid" // Another example tune
};
SdFat SD;
MD_MIDIFile SMF;
IRrecv irrecv(14); // Define the IR receiver pin
decode_results results;
#define NOTE_DELAY_MS 50 // 50 ms delay between notes
void midiCallback(midi_event *pev)
{
if (pev->size > 0 && (pev->data[0] == 0x80 || pev->data[0] == 0x90))
{
playNote(pev->data[1], pev->data[2]);
delay(NOTE_DELAY_MS); // Delay after each note to create separation
}
}
void playNote(int pitch, int velocity)
{
int state = (velocity > 10) ? LOW : HIGH;
bool found = false;
for (uint8_t n = 0; n < ARRAY_SIZE(noteData) && !found; n++)
{
for (uint8_t m = 0; m < NOTE_SIZE && !found; m++)
{
if (pitch == noteData[n].note[m])
{
found = true;
digitalWrite(noteData[n].pin, state);
}
}
}
}
void sysexCallback(sysex_event *pev)
{
// Ignored as sysex events are not handled in this setup
}
void midiSilence()
{
// Turn everything off on every channel.
midi_event ev;
// All sound off
ev.size = 0;
ev.data[ev.size++] = 0xb0;
ev.data[ev.size++] = 120;
ev.data[ev.size++] = 0;
for (ev.channel = 0; ev.channel < 16; ev.channel++)
midiCallback(&ev);
}
// Function to set all relay pins to LOW
void resetRelayPins()
{
for (uint8_t i = 0; i < ARRAY_SIZE(noteData); i++)
{
digitalWrite(noteData[i].pin, LOW);
}
}
void setup()
{
// Set up motor pins
for (uint8_t i = 0; i < ARRAY_SIZE(noteData); i++)
{
pinMode(noteData[i].pin, OUTPUT);
}
// Initialize Serial, SD, and IR
Serial.begin(SERIAL_RATE);
if (!SD.begin(SD_SELECT, SPI_FULL_SPEED))
{
while (true); // Halt if SD initialization fails
}
SMF.begin(&SD);
SMF.setMidiHandler(midiCallback);
SMF.setSysexHandler(sysexCallback);
irrecv.enableIRIn(); // Initialize IR receiver
}
void loop()
{
static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
static uint16_t currTune = 0; // Start with the first song
static uint32_t timeStart;
// Check for IR remote input
if (irrecv.decode(&results))
{
long int decCode = results.value;
irrecv.resume(); // Receive the next value
// Check if the up or down buttons are pressed
if (decCode == 0xFF10EF) // Up button (replace with actual code)
{
// Go back to the previous song
if (currTune == 0)
currTune = ARRAY_SIZE(tuneList) - 1; // Loop to the last song
else
currTune--;
}
else if (decCode == 0xFF5AA5) // Down button (replace with actual code)
{
// Go forward to the next song
currTune++;
if (currTune >= ARRAY_SIZE(tuneList))
currTune = 0; // Loop back to the first song
}
// Stop the current song before starting a new one
if (state == S_PLAYING || state == S_END)
{
SMF.close(); // Close the current MIDI file
midiSilence(); // Turn off all notes
}
// Reset relay pins before starting the new song
resetRelayPins();
// Restart the song
state = S_IDLE;
}
switch (state)
{
case S_IDLE: // Now idle, set up the next tune
{
int err;
// Use the next file name and play it
err = SMF.load(tuneList[currTune]);
if (err != MD_MIDIFile::E_OK)
{
timeStart = millis();
state = S_WAIT_BETWEEN;
}
else
{
state = S_PLAYING;
}
}
break;
case S_PLAYING: // Play the file
if (!SMF.isEOF())
{
if (SMF.getNextEvent());
}
else
state = S_END;
break;
case S_END: // Done with this one
SMF.close();
midiSilence();
timeStart = millis();
state = S_WAIT_BETWEEN;
break;
case S_WAIT_BETWEEN: // Signal finished with a pause
if (millis() - timeStart >= 2000)
state = S_IDLE;
break;
default:
state = S_IDLE;
break;
}
}