| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • You already know Dokkio is an AI-powered assistant to organize & manage your digital files & messages. Very soon, Dokkio will support Outlook as well as One Drive. Check it out today!

View
 

NanoPiano

Page history last edited by Cynthia De Dalmady 10 years, 10 months ago

The Nano Piano:

 

I first got the idea by watching my dorm mates playing the large piano in the dorm. I live on the first floor os I always hear the piano whenever it is being played, even when I'm trying to sleep. Now I would love to learn to play the piano, but I would feel bad practicing on the large piano where everyone could hear my awful playing. So it inspired me to build a small scale piano to help me practice. 

 

Design:

 

 

 

 

 

Prototype:

 

Schematics of the different circuits:

 

Mp3->4050->Arduino

 

LCD -> Arduino

 

Switches-> shift registers

 

 

Final circuit:

 

 

Mechanics:

Piano.ai

 

Display.ai

 

Code:

Resources used:

http://www.gammon.com.au/forum/?id=11979 for the shift registers

https://www.sparkfun.com/Code/MIDI_Example.pde for Midi player

 

 

#include <avr/pgmspace.h>

#include <SPI.h>

#include <VS1053.h>

#include <LiquidCrystal.h>

#include <SoftwareSerial.h>

SoftwareSerial mySerial(5, 6); //Soft TX on 3, we don't use RX in this code

 

VS1053 player(A0, A1, A2, -1);

VS1053::RtMidi midi(player);

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

 

#include <bitBangedSPI.h>

 

bitBangedSPI bbSPI (bitBangedSPI::NO_PIN, 9, 10);  // MOSI, MISO, SCK

 

const double sensorMin = 0;

const double sensorMax = 1024;

int volPin = A3;

int octPin = A4;

int instrPin = A5;

const int numReadings = 20;

int readingVol[numReadings];      // the readings from the analog input

int index = 0;                  // the index of the current reading

int totalVol = 0;                  // the running total

int averageVol = 0; 

 

int readingInstr[numReadings];

int totalInstr = 0;                  

int averageInstr = 0; 

 

const byte LATCH = 8;

byte optionSwitch1;

byte oldOptionSwitch1; // previous state

byte optionSwitch2;

byte oldOptionSwitch2;

byte optionSwitch3;

byte oldOptionSwitch3;

byte vol = 0x07;

int oct = 0;

int onvel = 60;

int offvel = 60;

#define encoder0PinA  0

#define encoder0PinB  1

 

byte instr = 0;

 

uint8_t notes[6][17] =

{

{24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40},

{36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52},

{48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64},

{60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76},

{72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88},

{84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100}

};

 

 

byte resetMIDI = -1; //Tied to VS1053 Reset line

byte ledPin = 13; //MIDI traffic inidicator

 

 

void setup(){

  bbSPI.begin ();

  Serial.begin (115200);

  mySerial.begin(31250);

  delay(2000); 

  pinMode(resetMIDI, OUTPUT);

  digitalWrite(resetMIDI, LOW);

  delay(100);

  digitalWrite(resetMIDI, HIGH);

  delay(100);

  lcd.begin(16, 2);

  pinMode (LATCH, OUTPUT);

  digitalWrite (LATCH, HIGH);

  SPI.begin();

  player.begin();

  midi.begin();

  player.setVolume(0x0);

  midi.selectDrums(0);

 

 

  updateScreen();

 

}

 

void loop(){

  talkMIDI(0xB0, 0x07, vol);

 talkMIDI(0xB0, 0, 0x00); //Default bank GM1

 talkMIDI(0xC0, instr, 0); //Set instrument number. 0xC0 is a 1 data byte command

 

 digitalWrite (LATCH, LOW);    // pulse the parallel load latch

  digitalWrite (LATCH, HIGH);

  optionSwitch1 = bbSPI.transfer (0);

  optionSwitch2 = bbSPI.transfer (0);

  optionSwitch3 = bbSPI.transfer (0);

 

  byte mask = 1;

  for (int i = 0; i <= 7; i++)

    {

    if ((optionSwitch3 & mask) != (oldOptionSwitch3 & mask))

      {

     Serial.print ("Switch 1:");

      Serial.print (i);

      Serial.print (" now ");

      Serial.println ((optionSwitch3 & mask) ? "closed" : "open");

      (optionSwitch3 & mask) ?  noteOn(0,notes[oct][i],onvel) :noteOff(0,notes[oct][i],offvel);

 

      }  // end of bit has changed

    mask <<= 1;  

    }  // end of for each bit

 

  oldOptionSwitch3 = optionSwitch3;

  delay(20);

 

  mask = 1;

  for (int i = 6; i <= 13; i++)

    {

    if ((optionSwitch2 & mask) != (oldOptionSwitch2 & mask))

      {

      Serial.print ("Switch 2:");

      Serial.print (i);

      Serial.print (" now ");

      Serial.println ((optionSwitch2 & mask) ? "closed" : "open");

      (optionSwitch2 & mask) ?  noteOn(0,notes[oct][i],onvel) :noteOff(0,notes[oct][i],offvel);

 

      }  // end of bit has changed

    mask <<= 1;  

    }  // end of for each bit

 

  oldOptionSwitch2 = optionSwitch2;

  delay (20);   // debounce

 

    mask = 1;

  for (int i = 12; i <= 19; i++)

    {

    if ((optionSwitch1 & mask) != (oldOptionSwitch1 & mask))

      {

      Serial.print ("Switch 3:");

      Serial.print (i);

      Serial.print (" now ");

      Serial.println ((optionSwitch1 & mask) ? "closed" : "open");

      (optionSwitch1 & mask) ?  noteOn(0,notes[oct][i],onvel) :noteOff(0,notes[oct][i],offvel);

 

      }  // end of bit has changed

    mask <<= 1;  

    }  // end of for each bit

 

  oldOptionSwitch1 = optionSwitch1;

  delay (20); 

 

 totalVol= totalVol - readingVol[index];         

  // read from the sensor:  

  readingVol[index] = analogRead(volPin); 

  // add the reading to the total:

  totalVol= totalVol + readingVol[index];                                

  // calculate the average:

  averageVol = totalVol / numReadings; 

 

 totalInstr = totalInstr - readingInstr[index];         

  // read from the sensor:  

  readingInstr[index] = analogRead(instrPin); 

  // add the reading to the total:

  totalInstr= totalInstr + readingInstr[index];       

  // advance to the next position in the array:  

  index = index + 1;                    

 

  // if we're at the end of the array...

  if (index >= numReadings)              

    // ...wrap around to the beginning: 

    index = 0;                           

 

  // calculate the average:

  averageInstr = totalInstr / numReadings; 

 

 

 

  byte nvol = map(averageVol, sensorMin,sensorMax, 127 , 0);

  int noct = map(analogRead(octPin), sensorMin, sensorMax, 5 , 0);

  byte ninstr = map(averageInstr,sensorMin,sensorMax, 127, -1);

  if(nvol != vol || noct != oct||ninstr != instr){

      static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

  // If interrupts come faster than 200ms, assume it's a bounce and ignore

  if (interrupt_time - last_interrupt_time > 200) 

  {

    vol = nvol;

    oct = noct;

    instr = ninstr;

    updateScreen();

  }

  last_interrupt_time = interrupt_time;

 

  }

 

}

 

void updateScreen(){

  lcd.clear();

  lcd.setCursor(0,0);

  lcd.print("Instr");

  lcd.setCursor(7,0);

  lcd.print("Oct");

  lcd.setCursor(12,0);

  lcd.print("Vol");

 

  lcd.setCursor(0,1);

  lcd.print(instr);

 

  lcd.setCursor(7,1);

  lcd.print(oct);

 

  lcd.setCursor(12,1);

  lcd.print(vol);

 

  delay(250);

 

}

 

 

 

//Send a MIDI note-on message.  Like pressing a piano key

//channel ranges from 0-15

void noteOn(byte channel, byte note, byte attack_velocity) {

  talkMIDI( (0x90 | channel), note, attack_velocity);

}

 

//Send a MIDI note-off message.  Like releasing a piano key

void noteOff(byte channel, byte note, byte release_velocity) {

  talkMIDI( (0x80 | channel), note, release_velocity);

}

 

//Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127

void talkMIDI(byte cmd, byte data1, byte data2) {

  digitalWrite(ledPin, HIGH);

  mySerial.write(cmd);

  mySerial.write(data1);

 

  //Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes 

  //(sort of: http://253.ccarh.org/handout/midiprotocol/)

  if( (cmd & 0xF0) <= 0xB0)

    mySerial.write(data2);

 

  digitalWrite(ledPin, LOW);

}

 

Final Product!

 

 

( The speakers in the second part were very soft for some reason, but you can still hear it.)

 

 

 

Comments (0)

You don't have permission to comment on this page.