I was attempting Alastair's keyboard Arduino puzzle. I just attached the keyboard to a MIDI shield on the Arduino and ran the code. Surprisingly Midi.Read was giving output in the Serial Monitor.
I tried adding some comments to the output and now Midi.Read puts out nothing. I reverted back to the original code. Nothing. I switched boards and ran into all kinds of errors uploading and with ports that aren't relevant but added to my frustration.
Anyway, the code is uploaded again and still I'm getting nothing from Midi.Read. I can put a comment in the loop to show it's at least getting there. Also the comment comes out as backwards question marks. I learned that maybe I need to add Serial.begin (9600) but that didn't work.
Here is an image of my set up. As I explained it's just the Arduino Uno, a MIDI shield a MIDI cable and a Rockband 3 Keyboard
Here is the code but it probably won't help since I can verify it was working (kind of) at one point with the same code.
/**
* "Play it once, Sam" Arduino MIDI-sequence puzzle
* Alastair Aitchison (c) 2018
*
* This puzzle requires the user to play a melody on a MIDI device.
* Each correct note played will light up an LED on a WS2812B LED strip.
* When the entire melody has been played correctly, a relay will be activated (releasing a maglock etc.)
* Playing an incorrect note means the sequence must be restarted from the beginning
*/
// INCLUDES
// FastLED library used for controlling the pixel strip. Download from http://fastled.io/
#include <FastLED.h>
// For handling MIDI messages. Install from Sketch -> Include Library -> Manage Libraries -> "MIDI Library by Forty Seven Effects"
#include <MIDI.h>
// This library defines a simple list structure to keep track of multiple notes played simultaneously by the player
#include "noteList.h"
// Lookup table to define tone frequency of musical notes. e.g. Concert A = 440Hz
#include "pitches.h"
// CONSTANTS
// Define the number of steps in the sequence that the player must follow
const byte numSteps = 9;
// The correct melody required to solve the puzzle.
// Defined using MIDI note numbers - see http://www.inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies
const byte melody[numSteps] = {50, 55, 58, 57, 55, 62, 61, 58, 55 };
// The MIDI note of the key which will reset the puzzle when pressed. 70 = Bb4
const byte resetPitch = 70;
// This pin will be driven HIGH to release a lock when puzzle is solved
const byte lockPin = A1;
// This pin will have a piezo buzzer connected to it to produce crude audio output
const byte audioOutPin = 9;
// The maximum number of notes that we'll keep track of being pressed at once
const unsigned int maxHeldNotes = 16;
// GLOBALS
// This macro creates an instance of the MIDI interface using the hardware serial ports (0 and 1)
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
// Create a list structure to keep track of the notes currently being held
MidiNoteList<maxHeldNotes> midiNotes;
// What step of the melody is the player currently on?
int currentStep = 0;
// Create an array to hold the RGB values of each LED in the strip
CRGB leds[numSteps];
// Track the overall state of the puzzle
enum PuzzleState {Initialising, Running, Solved};
PuzzleState puzzleState = Initialising;
/**
* Called when the program first starts, or each time it is reset
*/
void setup() {
// The handleNoteOn callback function will be triggered on receipt of a MIDI NoteOn msg
MIDI.setHandleNoteOn(handleNoteOn);
// The handleNoteOff callback function will be triggered on receipt of a MIDI NoteOff msg
MIDI.setHandleNoteOff(handleNoteOff);
// Initiate MIDI communications - my keyboard input uses MIDI channel 1
MIDI.begin(1);
// Initialise the LED strip on pin A0. WS2812B strip uses GRB byte-ordering.
FastLED.addLeds<WS2812B, A0, GRB>(leds, numSteps);
FastLED.clear();
// Initialise the relay pin. Setting to low will make the NO and C connected, securing the lock
pinMode(lockPin, OUTPUT);
digitalWrite(lockPin, LOW);
// Set the puzzle state to running
puzzleState = Running;
}
/**
* Callback function to handle a new note being pressed
*/
void handleNoteOn(byte channel, byte pitch, byte velocity) {
// Add the note value to our list of notes being played
midiNotes.add(MidiNote(pitch, velocity));
// Play the note on the buzzer
tone(audioOutPin, sNotePitches[pitch]);
// If the puzzle is in progress..
if(puzzleState == Running) {
// Has the correct note been pressed?
if(pitch == melody[currentStep]) {
// Move on to the next note in the melody
currentStep++;
// Have we reached the last note in the melody?
if(currentStep == numSteps) {
// Puzzle has been solved!
onSolve();
}
}
// Has an incorrect note been pressed?
else {
// Flash the LEDs red
for(int i=currentStep; i<numSteps; i++){
leds[i] = CRGB::Red;
FastLED.show();
FastLED.delay(20);
}
// Clear the display
FastLED.clear();
// Start from the beginning of the sequence again
currentStep = 0;
}
}
// If the puzzle is solved, has the reset key being pressed?
else if(puzzleState == Solved && pitch == resetPitch) {
onReset();
}
}
/**
* Callback function to handle a note being released
*/
void handleNoteOff(byte channel, byte pitch, byte velocity) {
// Remove this note from the list of notes being held
midiNotes.remove(pitch);
// If no other notes are being held
if (midiNotes.empty()) {
// Turn off the audio
noTone(audioOutPin);
}
// If there are other notes still being held down
else {
// Get the last note to be added (i.e. the most recently played one)
byte currentNote = 0;
if (midiNotes.getLast(currentNote)) {
// And play that note instead
tone(audioOutPin, sNotePitches[currentNote]);
}
}
}
/**
* Updates the LED strip based on the current game state
*/
void updateDisplay() {
switch(puzzleState){
// If the game is in progress
case Running:
// Turn on the number of LEDs corresponding to the current step
for(int i=0; i<numSteps; i++){
leds[i] = (i < currentStep ? CRGB::Green : CRGB::Black);
}
break;
// If the game has been completed
case Solved:
// Show a chase sequence of blue LEDs
fadeToBlackBy(leds, numSteps, 20);
int pos = beatsin16(12, 0, numSteps-1);
leds[pos] = CRGB::Blue;
FastLED.delay(30);
break;
}
// Update the display
FastLED.show();
}
/**
* This function is called when the puzzle is reset
*/
void onReset(){
// Stop the audio
noTone(audioOutPin);
// Light the LEDs up yellow
for(int i=0; i<numSteps; i++){
leds[i] = CRGB::Yellow;
FastLED.show();
FastLED.delay(20);
leds[i] = CRGB::Black;
}
// Then clear the LED display
FastLED.clear();
// Secure the lock again
digitalWrite(lockPin, LOW);
// And update the puzzle state
puzzleState = Running;
}
/**
* This function gets called when the puzzle is solved
*/
void onSolve(){
// Release the lock
digitalWrite(lockPin, HIGH);
// Set the puzzle state
puzzleState = Solved;
}
/**
* Main program loop
*/
void loop() {
// The read() function checks for incoming MIDI messages and automatically calls the assigned NoteOn or NoteOff callback
// if a matching input has been received - no further action required!
MIDI.read();
// Updates the LED strip based on the current state of the puzzle
updateDisplay();
}