Assignment Overview
Learning Objectives
Work Completed
Detailed Documentation
Code written using Cursor IDE and compiled/uploaded with Arduino IDE
/* * QPAD21 MIDI Piano Controller * * Touch pads play piano notes and display the corresponding keys * Simple MIDI output for testing with Hairless MIDI * * Developed in Cursor IDE, compiled with Arduino IDE */ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Adafruit_FreeTouch.h> // Display configuration #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Hardware pins #define PIN_LED 15 #define N_TOUCH 6 #define THRESHOLD 750 // Touch pad configuration uint8_t touch_pins[N_TOUCH] = {2, 3, 4, 5, 6, 7}; Adafruit_FreeTouch* touch_devices[N_TOUCH]; // Touch state tracking int touch_values[N_TOUCH] = {0, 0, 0, 0, 0, 0}; bool pin_touched_now[N_TOUCH] = {false, false, false, false, false, false}; bool pin_touched_past[N_TOUCH] = {false, false, false, false, false, false}; // Debouncing unsigned long last_touch_time[N_TOUCH] = {0, 0, 0, 0, 0, 0}; #define DEBOUNCE_DELAY 300 // 300ms debounce delay // Piano notes (MIDI note numbers) - matches Microsoft GS Wavetable Synth // These are the standard piano notes that sound good on Microsoft GS Wavetable Synth byte piano_notes[N_TOUCH] = {60, 62, 64, 65, 67, 69}; // C4, D4, E4, F4, G4, A4 String note_names[N_TOUCH] = {"C4", "D4", "E4", "F4", "G4", "A4"}; // MIDI Configuration #define MIDI_CHANNEL 1 void update_touch() { Ptc *ptc = ((Ptc *)PTC); for (int i = 0; i < N_TOUCH; i++) { touch_devices[i]->begin(); touch_values[i] = touch_devices[i]->measure(); ptc->CTRLA.bit.ENABLE = 0; ptc->CTRLA.bit.SWRESET = 1; pin_touched_past[i] = pin_touched_now[i]; pin_touched_now[i] = touch_values[i] > THRESHOLD; } } void send_midi_note(byte note, byte velocity) { // Send MIDI Note On message Serial.write(0x90 | (MIDI_CHANNEL - 1)); // Note On, Channel 1 Serial.write(note); Serial.write(velocity); } void send_midi_note_off(byte note) { // Send MIDI Note Off message Serial.write(0x80 | (MIDI_CHANNEL - 1)); // Note Off, Channel 1 Serial.write(note); Serial.write((byte)0); // Velocity 0 } void handle_touch_events() { unsigned long current_time = millis(); for (int i = 0; i < N_TOUCH; i++) { // Note On when touched if (pin_touched_now[i] && !pin_touched_past[i] && (current_time - last_touch_time[i] > DEBOUNCE_DELAY)) { last_touch_time[i] = current_time; send_midi_note(piano_notes[i], 127); // Send Note On digitalWrite(PIN_LED, HIGH); } // Note Off when released if (!pin_touched_now[i] && pin_touched_past[i]) { send_midi_note_off(piano_notes[i]); // Send Note Off digitalWrite(PIN_LED, LOW); } } } void draw_piano_keyboard() { display.clearDisplay(); // Draw title display.setTextSize(1); display.setCursor(0, 0); display.print("QPAD21 Piano"); // Piano keyboard dimensions int key_width = 18; int key_height = 30; int start_x = 5; int start_y = 15; // Draw white keys (6 keys) for (int i = 0; i < N_TOUCH; i++) { int x = start_x + (i * key_width); int y = start_y; // Draw key background if (pin_touched_now[i]) { // Key pressed - filled rectangle display.fillRect(x, y, key_width - 1, key_height, SSD1306_WHITE); // Draw note name in black (inverted) display.setTextColor(SSD1306_BLACK); display.setCursor(x + 2, y + 8); display.print(note_names[i]); display.setTextColor(SSD1306_WHITE); } else { // Key not pressed - outline rectangle display.drawRect(x, y, key_width - 1, key_height, SSD1306_WHITE); // Draw note name in white display.setTextColor(SSD1306_WHITE); display.setCursor(x + 2, y + 8); display.print(note_names[i]); } } // Draw black keys (sharps) - smaller keys on top int black_key_width = 12; int black_key_height = 20; // Draw black keys for sharps (C#, D#, F#, G#) int black_keys[] = {0, 1, 3, 4}; // Q0, Q1, Q3, Q4 have sharps String black_note_names[] = {"C#", "D#", "F#", "G#"}; for (int i = 0; i < 4; i++) { int key_index = black_keys[i]; int x = start_x + (key_index * key_width) + (key_width / 2) - (black_key_width / 2); int y = start_y; // Draw black key display.fillRect(x, y, black_key_width, black_key_height, SSD1306_WHITE); // Draw note name in black display.setTextColor(SSD1306_BLACK); display.setCursor(x + 1, y + 5); display.print(black_note_names[i]); display.setTextColor(SSD1306_WHITE); } // Draw touch pad indicators at bottom display.setCursor(0, 50); display.print("Q0 Q1 Q2 Q3 Q4 Q5"); // Draw pressed indicators display.setCursor(0, 58); for (int i = 0; i < N_TOUCH; i++) { if (pin_touched_now[i]) { display.print(" * "); } else { display.print(" - "); } } display.display(); } void setup() { // Initialize serial for MIDI output Serial.begin(31250); // Initialize touch devices for (int i = 0; i < N_TOUCH; i++) { touch_devices[i] = new Adafruit_FreeTouch(touch_pins[i], OVERSAMPLE_1, RESISTOR_100K, FREQ_MODE_NONE); } // Initialize LED pinMode(PIN_LED, OUTPUT); digitalWrite(PIN_LED, LOW); // Initialize display if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { // Display initialization failed - flash LED for (int i = 0; i < 10; i++) { digitalWrite(PIN_LED, HIGH); delay(100); digitalWrite(PIN_LED, LOW); delay(100); } } // Initial display display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(10, 25); display.println("QPAD21 Piano"); display.setCursor(10, 35); display.println("Ready!"); display.display(); delay(1000); } void loop() { // Update touch sensors update_touch(); // Handle touch events handle_touch_events(); // Update display draw_piano_keyboard(); // Small delay delay(50); }
Images
Reflection
Links