Piano
- Door Pierre Van de Velde
Wat is het?
Om het laatste deel van de puzzel op te lossen moeten de spelers het ontvangen muziekpartituur van Mozart correct spelen op een piano. Het systeem moet de ingedrukte toetsen detecteren en controleren of de spelers de juiste melodie in de correcte volgorde spelen. Wanneer de volledige melodie foutloos wordt uitgevoerd, moet de microcontroller dit doorgeven aan de hoofdcomputer zodat de puzzel als voltooid wordt beschouwd.
Daarnaast moet het systeem kunnen communiceren met de hoofdcomputer via bluetooth. De hoofdcomputer kan de puzzel starten of stoppen en ontvangt een bevestiging wanneer de melodie succesvol werd gespeeld.
Ontwerpkeuzes
Mijn eerste idee was om rechtstreeks verbinding te maken met de interne elektronica van het keyboard door draadjes aan de toetsen te solderen. Na het openen van het keyboard bleek dit echter veel complexer dan verwacht. Op aanraden van een vriend die veel ervaring heeft met muziekapparatuur ben ik daarom MIDI gaan onderzoeken.
Via een Arduino MIDI Shield kon de MIDI-uitgang van het keyboard rechtstreeks worden uitgelezen. Hierdoor werd elke gespeelde noot als seriële data ontvangen door de microcontroller. Op basis van deze gegevens kon gecontroleerd worden of de speler de juiste melodie speelde.>
Omdat de bluetoothmodule eveneens seriële communicatie gebruikt, ontstond een conflict met de standaard RX- en TX-pinnen van de microcontroller. Dit werd opgelost door gebruik te maken van SoftwareSerial. Hierdoor konden twee andere pinnen als extra RX- en TX-pinnen worden gebruikt voor de bluetoothverbinding.
De MIDI-verbinding bleef aangesloten op de hardwarematige RX- en TX-pinnen van de microcontroller. MIDI communiceert met een hoge baudrate van 115200 baud, waardoor de hardwarematige seriële poort betrouwbaarder is dan SoftwareSerial. Op deze manier wordt vermeden dat noten verloren gaan tijdens het uitlezen van het keyboard. De bluetoothmodule verstuurt enkel eenvoudige hexadecimale codes en kan daarom probleemloos via SoftwareSerial werken.
Componenten
- Elektrisch keyboard met MIDI-uitgang
- Arduino microcontroller
- Arduino MIDI Shield
- Bluetoothmodule
- SoftwareSerial-bibliotheek
- MIDI-bibliotheek
MIDI Shield
Foto MIDI Shield
Note: Wij gebruiken enkel de MIDI IN op de adapter voor dit project, er is echter ook een poort voor MIDI OUT en MIDI THRU.
Aansluitingen
- GND → GND
- 5V → 5V
- RX → RX
- TX (D0 op het schema) → TX
Schema
Stappenplan
- De hoofdcomputer start de piano-puzzel via bluetooth.
- De microcontroller ontvangt het startsignaal en activeert de puzzel.
- De speler speelt noten op het elektrische keyboard.
- De MIDI Shield ontvangt de MIDI-gegevens van het keyboard.
- De microcontroller leest de MIDI-berichten uit en zet elke noot om naar een nootnaam (DO, RE, MI, FA, SOL, LA of SI).
- De gespeelde noot wordt vergeleken met de volgende verwachte noot uit de opgeslagen melodie.
- Wanneer een foutieve noot wordt gespeeld, wordt de voortgang gereset.
- Wanneer alle noten van de melodie in de juiste volgorde gespeeld zijn, wordt de puzzel als voltooid beschouwd.
- De microcontroller verstuurt een bevestigingscode naar de hoofdcomputer.
- De hoofdcomputer kan vervolgens het volgende onderdeel van de escape room activeren.
Code
#include <SoftwareSerial.h>
#include <MIDI.h>
#define bluetoothRX 2
#define bluetoothTX 3
SoftwareSerial mySerial(bluetoothRX, bluetoothTX);
byte status = 0x0;
MIDI_CREATE_DEFAULT_INSTANCE();
const char* melody[] = { //een soort dictionary met alle verschillende soorten noten.
"DO", "DO",
"SOL", "SOL",
"LA", "LA",
"SOL",
"FA", "FA",
"MI", "MI",
"RE", "RE",
"DO"
};
const int melodyLength = sizeof(melody) / sizeof(melody[0]);
int currentPosition = 0;
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
mySerial.begin(9600);
MIDI.begin(MIDI_CHANNEL_OMNI);
Serial.println("MIDI test");
}
const char* noteName(byte midiNote) { //Er zijn 12 noten maar er zijn 48 toetsen. De toetsen herhalen hetzelfde 12 noten patroon.
switch (midiNote % 12) {
case 0: return "DO"; // C
case 1: return "DO#";
case 2: return "RE"; // D
case 3: return "RE#";
case 4: return "MI"; // E
case 5: return "FA"; // F
case 6: return "FA#";
case 7: return "SOL"; // G
case 8: return "SOL#";
case 9: return "LA"; // A
case 10: return "LA#";
case 11: return "SI"; // B
}
return "?";
}
void loop() {
if (mySerial.available()) { //inlezen wat we krijgen van the orchestrator (computer), gamestatus aanpassen
byte received = mySerial.read();
if (received == 0x0) {
status = received;
digitalWrite(LED_BUILTIN, LOW);
mySerial.write(status);
} else if (received == 0x1) {
status = received;
digitalWrite(LED_BUILTIN, HIGH);
mySerial.write(status);
} else if (received == 0x21) {
mySerial.write(status);
}
}
if(status==0x1){ //0x1 = start game
if (!MIDI.read())
return;
// Alleen Note On
if (MIDI.getData2() <= 0)
return;
const char* played = noteName(MIDI.getData1());
if (strcmp(played, melody[currentPosition]) == 0) { //de gespeelde melody controleren
currentPosition++;
if (currentPosition == melodyLength) {
status = 0x11;
mySerial.write(status); //bevestigen dat het spel gecompleet is naar de orchestrator
currentPosition = 0;
}
} else {
//als de foutieve noot tegelijk de eerste noot is
if (strcmp(played, melody[0]) == 0)
currentPosition = 1;
else
currentPosition = 0;
}
}
}
// 0x0 game is gestopt
... (3 regels over)
Eindresultaat
