2023.10.23(pm): 아두이노 종이피아노 만들기

이 포스팅은 로봇만들기 봉사활동에 활용될 자료이며 아래 링크의 자료를 참고하여 작성되었습니다.

https://kocoafab.cc/make/view/197

이번시간에는 아두이노를 이용하여 피아노를 만들어보려고 하는데요, 우리 주변에서 흔하게 구할 수 있는 전기가 통하는 금속박(쿠킹호일 등)을 이용하여 소리가 나도록 만들어볼겁니다.

DIY: Arduino Based Continuous Touch Piano | Arduino Project Hub
Arduino Project

우선 가장 중요한 피아노 건반부터 잘라 붙입니다. 아래 사진을 참고하여 피아노 건반을 잘라서 붙여봅시다. 저는 시간이 없어서 빨리 만든다고 모양이 좋지는 않지만, 여러분들은 이왕이면 보기 좋게 잘라서 붙이길 바랍니다.

아래 회로도대로 아두이노와 건반을 브레드보드, 저항, 점퍼케이블을 이용해서 연결해줍니다.

다음으로 아두이노 Tools > Board > 에서 보드를 Mega2560 으로 선택합니다.

Tools > Port 에서 아두이노 보드 포트를 선택합니다.

라이브러리 매니저에 들어가서 CapacitiveSensor를 검색한 뒤 Install 해줍니다.

아두이노에는 아래와 같은 코드를 붙여넣습니다.

#include <CapacitiveSensor.h>

//CapacitiveSensor라이브러리를 사용하여 좌표값을 지정하여 객체생성
CapacitiveSensor   cs_2_3 = CapacitiveSensor(2,3);        
CapacitiveSensor   cs_2_4 = CapacitiveSensor(2,4);        
CapacitiveSensor   cs_2_5 = CapacitiveSensor(2,5);        
CapacitiveSensor   cs_6_7 = CapacitiveSensor(6,7);        
CapacitiveSensor   cs_6_8 = CapacitiveSensor(6,8);        
CapacitiveSensor   cs_6_9 = CapacitiveSensor(6,9);
CapacitiveSensor   cs_10_11 = CapacitiveSensor(10,11);        
CapacitiveSensor   cs_10_12 = CapacitiveSensor(10,12); 
CapacitiveSensor   cs_10_13 = CapacitiveSensor(10,13); 
CapacitiveSensor   cs_30_31 = CapacitiveSensor(30,31);        
CapacitiveSensor   cs_30_32 = CapacitiveSensor(30,32);        
CapacitiveSensor   cs_30_33 = CapacitiveSensor(30,33);        
CapacitiveSensor   cs_40_41 = CapacitiveSensor(40,41);        

int Do;
int Re;
int Mi;
int Pa;
int Sol;
int Ra;
int Si;
int Do_1;
int DoSharp;
int ReSharp;
int PaSharp;
int SolSharp;
int RaSharp;

byte ch = '9';

void setup()                    
{
   cs_2_4.set_CS_AutocaL_Millis(0xFFFFFFFF);
   Serial.begin(9600);
}

void loop()                    
{
    //손가락이 접촉되었을때 값을 측정. 접촉이 없을 경우 0을 반환하고 접촉이 있을 경우 0보다 큰값을 반환한다
    long total1 =  cs_2_3.capacitiveSensor(30);
    long total2 =  cs_2_4.capacitiveSensor(30);
    long total3 =  cs_2_5.capacitiveSensor(30);
    long total4 =  cs_6_7.capacitiveSensor(30);
    long total5 =  cs_6_8.capacitiveSensor(30);
    long total6 =  cs_6_9.capacitiveSensor(30);
    long total7 =  cs_10_11.capacitiveSensor(30);
    long total8 =  cs_10_12.capacitiveSensor(30);
    long total9 =  cs_10_13.capacitiveSensor(30);
    long total10 =  cs_30_31.capacitiveSensor(30);
    long total11 =  cs_30_32.capacitiveSensor(30);
    long total12 =  cs_30_33.capacitiveSensor(30);
    long total13 =  cs_40_41.capacitiveSensor(30);
    
    //각 건반마다 특정값 이상으로 출력될 경우 그 건반 상태를 1로 변경(신체와 접촉 상태)
    //접촉이 없을 경우 건반 상태를 0으로 변경
    if(total1 > 20)  Do = 1;
    else             Do = 0;
    
    if(total2 > 20)  Re = 1;
    else             Re = 0;
    
    if(total3 > 20)  Mi = 1;
    else             Mi = 0;
    
    if(total4 > 20)  Pa = 1;
    else             Pa = 0;
    
    if(total5 > 20)  Sol = 1;
    else             Sol = 0;

    if(total6 > 20)  Ra = 1;
    else             Ra = 0;

    if(total7 > 20)  Si = 1;
    else             Si = 0;
    
    if(total8 > 20)  Do_1 = 1;
    else             Do_1 = 0;
    
    if(total9 > 20)  DoSharp = 1;
    else             DoSharp = 0;
    
    if(total10 > 20) ReSharp = 1;
    else             ReSharp = 0;

    if(total11 > 20) PaSharp = 1;
    else             PaSharp = 0;
    
    if(total12 > 20) SolSharp = 1;
    else             SolSharp = 0;

    if(total13 > 20) RaSharp = 1;
    else             RaSharp = 0;

    //시리얼 통신을 통해 프로세싱으로 전송
    Serial.write(ch);
    Serial.write(Do);
    Serial.write(Re);
    Serial.write(Mi);
    Serial.write(Pa);
    Serial.write(Sol);
    Serial.write(Ra);
    Serial.write(Si);
    Serial.write(Do_1);
    Serial.write(DoSharp);
    Serial.write(ReSharp);
    Serial.write(PaSharp);
    Serial.write(SolSharp);
    Serial.write(RaSharp);

    delay(15);  //안정성을 위해 지연시간을 준다
}

컴파일 및 업로드를 해줍니다.

그 다음으로는 프로세싱을 이용해야합니다.

프로세싱이란  2001년 MIT 미디어랩에서 Ben Fry와 Casey Reas가 개발한 오픈소스 기반 프로그램인데, 시각화 인터페이스 프로그램을 개발하는 환경을 제공하는 것이 목적입니다.
컴퓨터 프로그래밍의 본질을 시각적 개념으로 프로그래머가 아닌 사람들에게 교육할 목적으로 뉴 미디어 아트, 시각 디자인 공동체를 위해 개발하였습니다.
JAVA를 기반으로 하고 있고 외부 인터페이스 지원이 가능해서 아두이노 센서 데이터를 serial로 전송받을 수 있습니다.

아래 프로세싱 홈페이지에 들어가서 프로세싱 프로그램을 다운받습니다.

https://processing.org/

다운받으면 압축을 풀고 실행파일을 실행시켜줍니다.

그러면 아래와 같이 프로세싱 환경이 구성됩니다.

라이브러리에 들어가서 minim이라는 라이브러리를 다운받습니다.
minim 라이브러리는 프로세싱에서 음악 파일을 재생시켜주는 라이브러리입니다. 다운로드하는데 시간이 꽤 걸립니다.

다운로드가 완료되면 프로세싱에 아래 코드를 입력합니다.

import processing.serial.*;
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;
Minim minim = new Minim(this);
AudioSample playDo;
AudioSample playRe;
AudioSample playMi;
AudioSample playPa;
AudioSample playSol;
AudioSample playRa;
AudioSample playSi;
AudioSample playDo_1;
AudioSample playDoSharp;
AudioSample playReSharp;
AudioSample playPaSharp;
AudioSample playSolSharp;
AudioSample playRaSharp;
int wait;
int Do;
int Re;
int Mi;
int Pa;
int Sol;
int Ra;
int Si;
int Do_1;
int DoSharp;
int ReSharp;
int PaSharp;
int SolSharp;
int RaSharp;
  int Last_Do;
  int Last_Re;
  int Last_Mi;
  int Last_Pa;
  int Last_Sol;
  int Last_Ra;
  int Last_Si;
  int Last_Do_1;
  int Last_DoSharp;
  int Last_ReSharp;
  int Last_PaSharp;
  int Last_SolSharp;
  int Last_RaSharp;
  
Serial myPort;
void setup(){
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0],9600);
  playDo = minim.loadSample("do.mp3");
  playRe = minim.loadSample("re.mp3");
  playMi = minim.loadSample("mi.mp3");
  playPa = minim.loadSample("pa.mp3");
  playSol = minim.loadSample("sol.mp3");
  playRa = minim.loadSample("ra.mp3");
  playSi = minim.loadSample("si.mp3");
  playDo_1 = minim.loadSample("do_1.mp3");
  playDoSharp = minim.loadSample("dosharp.mp3");
  playReSharp = minim.loadSample("resharp.mp3");
  playPaSharp = minim.loadSample("pasharp.mp3");
  playSolSharp = minim.loadSample("solsharp.mp3");
  playRaSharp = minim.loadSample("rasharp.mp3");
  Last_Do = 0;
  Last_Re = 0;
  Last_Mi = 0;
  Last_Pa = 0;
  Last_Sol = 0;
  Last_Ra = 0;
  Last_Si = 0;
  Last_Do_1 = 0;
  Last_DoSharp = 0;
  Last_ReSharp = 0;
  Last_PaSharp = 0;
  Last_SolSharp = 0;
  Last_RaSharp = 0;
}
void draw(){
  
  if(myPort.available() >=1) {      
    if(myPort.read() == '9') { 
      Do = myPort.read();
      Re = myPort.read();
      Mi = myPort.read();
      Pa = myPort.read();
      Sol = myPort.read();
      Ra = myPort.read();
      Si = myPort.read();
      Do_1 = myPort.read();
      DoSharp = myPort.read();
      ReSharp = myPort.read();
      PaSharp = myPort.read();
      SolSharp = myPort.read();
      RaSharp = myPort.read();
    } 
  }
  background(255);
  print(Do);
  print(" ");
  print(DoSharp);
  print(" ");
  print(Re);
  print(" ");
  print(ReSharp);
  print(" ");
  print(Mi);
  print(" ");
  print(Pa);
  print(" ");
  print(PaSharp);
  print(" ");
  print(Sol);
  print(" ");
  print(SolSharp);
  print(" ");
  print(Ra);
  print(" ");
  print(RaSharp);
  print(" ");
  print(Si);
  print(" ");
  print(Do);
  println(" ");
 if(Do > 0) {
    if(Last_Do <= 0) {
      playDo.trigger();
      Last_Do = 1;
    }
  }
  else{
    Last_Do = 0;
  }
  
if(Re > 0) {
    if(Last_Re <= 0) {
      playRe.trigger();
      Last_Re = 1;
    }
  }
  else{
    Last_Re = 0;
  }
  
   if(Mi > 0) {
    if(Last_Mi <= 0) {
      playMi.trigger();
      Last_Mi = 1;
    }
  }
  else{
    Last_Mi = 0;
  }
  
    if(Pa > 0) {
    if(Last_Pa <= 0) {
      playPa.trigger();
      Last_Pa = 1;
    }
  }
  else{
    Last_Pa = 0;
  }
  
 if(Sol > 0) {
    if(Last_Sol <= 0) {
      playSol.trigger();
      Last_Sol = 1;
    }
  }
  else{
    Last_Sol = 0;
  }
  
  if(Ra > 0) {
    if(Last_Ra <= 0) {
      playRa.trigger();
      Last_Ra = 1;
    }
  }
  else{
    Last_Ra = 0;
  }
  
   if(Si > 0) {
    if(Last_Si <= 0) {
      playSi.trigger();
      Last_Si = 1;
    }
  }
  else{
    Last_Si = 0;
  }
  
   if(Do_1 > 0) {
    if(Last_Do_1 <= 0) {
      playDo_1.trigger();
      Last_Do_1 = 1;
    }
  }
  else{
    Last_Do_1 = 0;
  }
  
   if(DoSharp > 0) {
    if(Last_DoSharp <= 0) {
      playDoSharp.trigger();
      Last_DoSharp = 1;
    }
  }
  else{
    Last_ReSharp = 0;
  }
  
  if(ReSharp > 0) {
    if(Last_ReSharp <= 0) {
      playReSharp.trigger();
      Last_ReSharp = 1;
    }
  }
  else {
    Last_ReSharp = 0;
  }
 if(PaSharp > 0) {
    if(Last_PaSharp <= 0) {
      playPaSharp.trigger();
      Last_PaSharp = 1;
    }
  }
  else{
    Last_PaSharp = 0;
  }
  
if(SolSharp > 0) {
    if(Last_SolSharp <= 0) {
      playSolSharp.trigger();
      Last_SolSharp = 1;
    }
  }
  else{
    Last_SolSharp = 0;
  }
  
   if(RaSharp > 0) {
    if(Last_RaSharp <= 0) {
      playRaSharp.trigger();
      Last_RaSharp = 1;
    }
  }
  else{
    Last_RaSharp = 0;
  }
}

음악재생파일을 아래 링크에서 다운로드 받은 후 프로세싱 파일과 같은 폴더에 넣어줍니다.

https://drive.google.com/drive/folders/1GPlxDGHjvrXRQsc5DhI9a0Ag70P3jYyz?usp=share_link

프로세싱에서 코드를 실행시켜줍니다.

딜레이가 심한데, 아직 이 문제는 해결중 …