エアドラムするとホントに演奏される(物理)

今回やること

机を叩くと、叩いた場所に合わせて実際にミニドラムが演奏されます。

このプログラムはnode.jsです。ブラウザで動作するものではありません。

node.jsはこちらから。

用意するもの

  • obniz ×3(距離センサ用 ×1, サーボモーター用 x2)
  • obnizの電源 x 3
  • サーボモーター x 2
  • GP2Y0A21YK0F(赤外線距離センサー) x 2
  • ミニドラムとバチ x1
  • 固定用の棒 x2
  • ビニールテープ x1
  • 机に敷く太鼓の位置が描かれた紙 x1

組み立て方

机側

下の表や図のようにobniz1つに対して赤外線距離センサーを2つ繋ぎ、obnizに電源を繋ぎます。

obniz GP2Y0A21YK0F (赤外線距離センサー)
0 [センサー1] Vcc
1 [センサー1] GND
2 [センサー1] signal
6 [センサー2] Vcc
7 [センサー2] GND
8 [センサー2] signal

配線できたら、画像のように赤外線距離センサーを横に並べて設置します。(画像は少し斜めに傾けています。)

左側のセンサのすぐ近くに手があるときは左の太鼓が、少し離れた位置にあるときは真ん中の太鼓が叩かれます。 右側のセンサのすぐ近くに手があるときはシンバルが、少し離れた位置にあるときは右側の太鼓が叩かれます。 どちらのセンサにとっても、さらに離れた位置にあるときはドラムが叩かれません。

ドラム側

下の表や図のようにobniz1つに対してサーボモーターを1つ繋ぎ、obnizに電源を繋ぎます。これを2セット用意します。このとき、それぞれのobnizに供給する電源はそれぞれ別に用意してください。

obniz サーボモーター
9 signal
10 Vcc
11 GND

サーボモーターにバチを取り付け、それをドラムに設置します。 1つは左の太鼓とシンバルを叩くように、もう1つは真ん中の太鼓と右の太鼓を叩くように設置します。

左の太鼓とシンバルを叩くようにバチを取り付ける
真ん中の太鼓と右の太鼓を叩くようにバチを取り付ける

これで完成です。プログラムを実行すると、机上で手を置いた位置によって対応する太鼓やシンバルが叩かれます。

プログラム

赤外線センサで得られた距離によって、サーボモーターの角度を変えてドラムを演奏します。

赤外線センサは、5cm以上の距離を測り結果を得ることができます。

var sensor = obniz.wired("GP2Y0A21YK0F", {vcc:0, gnd:1, signal:2})
sensor.start(function( distance ){
  console.log("distance " + distance + " mm")
})

赤外線センサの使い方はこちらへ。

サーボモーターの角度は簡単に変えることができます。

var servo = obniz.wired("ServoMotor", {gnd:0,vcc:1,signal:2});
servo.angle(90.0); // 角度を90度変えます。

サーボモーターの詳しい使い方はこちらへ。

完成したプログラム(node.js)

このプログラムはnode.jsです。ブラウザで動作するものではありません。

const Obniz = require("obniz");

const disObniz = new Obniz("OBNIZ_ID_FOR_DISTANCE_HERE");
const drumObnizes = [new Obniz("OBNIZ_ID_FOR_DRUM_1_HERE"), new Obniz("OBNIZ_ID_FOR_DRUM_2_HERE")];

//以下はドラム位置に対するサーボモータの角度です
//実際に設置してみて調節してください
const DEFAULT_SERVO_ANGLE = 40.0;
const BACK_SERVO_PARAM = {default: 40.0, leftdrum: 125.0, cymbal: 12.0};
const FRONT_SERVO_PARAM = {default: 40.0, centerdrum: 88.0, rightdrum: 25.0};

disObniz.onconnect = async () => {
	
	let disLeft = disObniz.wired("GP2Y0A21YK0F", {vcc:0, gnd:1, signal:2});
	let disRight = disObniz.wired("GP2Y0A21YK0F", {vcc:6, gnd:7, signal:8});
	let servoMotors = [];
	
	drumObnizes.forEach(async (obniz, index) => {
		
		if(drumObnizes[index].connectionState === 'connected'){
			
			await console.log(drumObnizes[index].connectionState);
			let servo = obniz.wired("ServoMotor", {signal:9,vcc:10, gnd:11});
			servoMotors[index] = servo;
			
		}else{
			
			await drumObnizes[index].wait(500);
			let servo = obniz.wired("ServoMotor", {signal:9,vcc:10, gnd:11});
			servoMotors[index] = servo;
			await console.log(drumObnizes[index].connectionState);
			
		}
		
		await console.log(servoMotors.length + "motors");
	});
	
	const rotateServo = async (area, servoAngle) => {
		
		if(servoMotors[area] === undefined || servoMotors[area] === null){
			return;
		}

		await servoMotors[area].angle(servoAngle);
		await resetServo(area);
	}
	
	const resetServo = async area => {
		servoMotors.forEach(async (servo, index) => {
			if(index === area || drumObnizes[index].connectionState !== 'connected'){
				return;
			}
			await servo.angle(DEFAULT_SERVO_ANGLE);
		});
	}
	
	//ループ1周目に反応しないように最初はエリア外に設定しておく
	let prevArea = {left: 2, right: 2};
	let nowArea = {left: 3, right: 3};
	
	//140mmごとにエリア分けする
	//left0 => 左上: 左太鼓
	//right0 => 右上: シンバル
	//left1 => 左下: 真ん中太鼓
	//right1 => 右下: 右太鼓
	const AREA_THRESHOLD = 140;
	
	while(true){

		
		let leftPos = await Math.floor(await disLeft.getWait() / AREA_THRESHOLD);
		let rightPos = await Math.floor(await disRight.getWait() / AREA_THRESHOLD);
		
		nowArea.left = leftPos;
		nowArea.right = rightPos;
		
		if(prevArea.left === nowArea.left && prevArea.right === nowArea.right){
			continue;
		}else if(prevArea.left > 1 && nowArea.left > 1 && prevArea.right > 1 && nowArea.right > 1){
			continue;
		}
		
		await console.log(nowArea);
		
		
		//エリア分け
		if(nowArea.left > 1 || nowArea.right > 1){
			await console.log("out of area");
			await resetServo(nowArea);
		}
		if(nowArea.left === 0){
			await console.log("left drum");
			await rotateServo(nowArea.left, BACK_SERVO_PARAM.leftdrum);
			
		}else if(nowArea.left === 1){
			await console.log("center drum");
			await rotateServo(nowArea.left, FRONT_SERVO_PARAM.centerdrum);
			
		}
		if(nowArea.right === 0){
			await console.log("cymbal");
			await rotateServo(nowArea.right, BACK_SERVO_PARAM.cymbal);
			
		}else if(nowArea.right===1){
			await console.log("right drum");
			await rotateServo(nowArea.right, FRONT_SERVO_PARAM.rightdrum);
			
		}
		
		prevArea.left = nowArea.left;
		prevArea.right = nowArea.right;
		
	}

	
}