Basic Information

This page allows you to edit the existing project (Aerial Synth). The edited project will replace the existing entry in the IoT Projects page. If you want to add a new entry based on the project, please fork from it instead.

* Photo URLs that end with any one of .jpg, .jpeg, .png or that are uploaded to the Gyazo web service in the PNG format (e.g.https://gyazo.com/hogehoge) are supported.

* Node.js entry point main: index.js and dependencies to the npm module dependencies: { f3js: (略) } will be automatically added and thus do not need to be in this form.

Card preview
Aerial Synth
'16/9/8 5:18
An electronic musical instrument, playable with an ultrasonic distance sensor and a rotary angle sensor

Private Project

This project is already public and cannot be made private.

Source Code

Provide the source code of a microcontroller or tiny computer in JavaScript. Node.js-based computers are supported. Require f3js package and use its API to design the device enclosure.

// 各種ドライバ var grove = require('jsupm_grove'); var buzzer = require('jsupm_buzzer'); var ultrasonic = require('jsupm_groveultrasonic'); var i2clcd = require('jsupm_i2clcd'); var lcd = require("jsupm_i2clcd"); // 各モジュールの初期化 var usonic = new ultrasonic.GroveUltraSonic(2); var rotary = new grove.GroveRotary(0); var buzzer = new buzzer.Buzzer(5); var button = new grove.GroveButton(3); var lcd = new i2clcd.Jhd1313m1(2, 0x3E, 0x62); // 距離から音量[0:1]への変換 (1だと大きすぎるので適当に) function distToVol(dist) { if (dist === 0) // 距離最小:音量最大 return 0.125; else if (dist > 1000) // 距離が一定以上:音量0 return 0; else // 中間 return 0.125 - (Math.sqrt(dist) / (10 * Math.sqrt(10)) / 8); } // 回転から周期の関数 ブザーに依り要変更? function rotToPer(rot) { return 1911 + rot * 2.58; } // 周期から音階の関数 function perToScale(per) { if (per > 4416) return 0; else if (per > 4168) return 1; else if (per > 3934) return 2; else if (per > 3713) return 3; else if (per > 3505) return 4; else if (per > 3308) return 5; else if (per > 3123) return 6; else if (per > 2947) return 7; else if (per > 2781) return 8; else if (per > 2626) return 9; else if (per > 2478) return 10; else if (per > 2339) return 11; else if (per > 2208) return 12; else if (per > 2084) return 13; else if (per > 1967) return 14; else return 15; } // 音量と周期の初期化 var vol = 0; var per = 0; // 距離の平滑化用配列2つ var dists = []; var sorted = []; // 距離初期化 for (var i = 0; i < 6; i++) dists.push(usonic.getDistance()); // 音量設定 function setVol() { dists.shift(); // 一番古いのを抜いて dists.push(usonic.getDistance()); // 現在の値を入れる sorted = dists.slice().sort(function (a, b) { if (a < b) return -1; if (a > b) return 1; return 0; }); vol = distToVol(sorted[2] + sorted[3] / 2); // 中央値 // console.log(vol, sorted); } // 周期設定 function setPer() { per = rotToPer(rotary.abs_value()); // console.log(per); } /* // for debug (音階,音量) var scales = ['A', 'AS', 'B', 'C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B', 'C']; function volToString(vol) { var str = ''; for (var i = 0; i < vol * 100; i++) str += '*'; return str; } */ currentScale = 0; function display() { lcd.setCursor(1, currentScale); lcd.write(" "); currentScale = perToScale(per) lcd.setCursor(1, currentScale); lcd.write("*"); } // vol, perにしたがって音を鳴らす function ring() { if (vol === 0.0) buzzer.stopSound(); else { buzzer.setVolume(vol); buzzer.playSound(per, 0); } // for debug (音階,音量をシリアル送信 l87-95が必要) // console.log(scales[perToScale(per)] + ' ' + volToString(vol)); } // 周期的実行のハンドラ var handleV, handleP, handleR, handleD; // 各処理の開始 function start() { handleV = setInterval(setVol, 5); handleP = setInterval(setPer, 5); handleR = setInterval(ring, 5); lcd.setColor(255,255,255); lcd.setCursor(0, 0); lcd.write("AoBCoDoEFoGoAoBC"); handleD = setInterval(display, 5); } // 各処理の停止 function stop() { clearInterval(handleV); clearInterval(handleP); clearInterval(handleR); clearInterval(handleD); lcd.setColor(0,0,0); lcd.clear(); setTimeout(function () { buzzer.stopSound(); }, 100); // うまく消えないことがある? } // 開始 start(); // 状態定数 var STATE_SWITCHING = 0; var STATE_ON = 1; var STATE_OFF = 2; // 状態初期化 var state = STATE_ON; // ボタンによる状態遷移 // 1度状態遷移したら1000msはSTATE_SWITCHINGにとどまり再遷移しない setInterval(function () { if (button.value() === 1) { if (state === STATE_ON) { state = STATE_SWITCHING; stop(); setTimeout(function () { state = STATE_OFF }, 1000); } if (state == STATE_OFF) { state = STATE_SWITCHING; start(); setTimeout(function () { state = STATE_ON }, 1000); } } }, 10); // ---------------ロジックここまで--------------- // ---------------ここから筐体設計--------------- var f3js = require('f3js') , x = 10 , y = 10 , width = 150 // 幅[mm][140, 200] , height = 100 // 奥行き[mm][95, 120] , thickness = 50 // 厚さ[mm][45, 100] , topmargin = 8; // 天面 var rect = f3js.drawJointRectangle(x, y, width, height); // 各部品の追加 //f3js.add(usonic, { x: x + 25 + topmargin + 0, y: y + height - 12.5 - topmargin + 0, rotation: 180 }); // NOTE: Changed to using layout manager. //f3js.add(rotary, { x: x + width - 10 - topmargin + 0, y: y + height - 10 - topmargin + 0 }); // NOTE: Changed to using layout manager. //f3js.add(buzzer, x + width / 2 + 10, y + height - 10 - topmargin, 90); // NOTE: Changed to using layout manager. //f3js.add(button, { x: x + 25 + topmargin + 0, y: y + 20 + topmargin + 0, rotation: 90 }); // NOTE: Changed to using layout manager. //f3js.add(lcd, x + width - 40 - topmargin , y + 20 + topmargin); // NOTE: Changed to using layout manager. var sensorLayout = f3js.drawLine(x + 25 + topmargin + 0, y + height - 10 - topmargin + 0, x + width - 10 - topmargin + 0, y + height - 10 - topmargin + 0); sensorLayout.layout = "distribute"; f3js.add(usonic, sensorLayout); f3js.add(buzzer, sensorLayout); f3js.add(rotary, sensorLayout).rotate(90); var buttonAndLCDLayout = f3js.drawLine(x + 25 + topmargin + 0, y + 20 + topmargin + 0, x + width - 40 - topmargin, y + 20 + topmargin); buttonAndLCDLayout.layout = "distribute"; f3js.add(button, buttonAndLCDLayout); f3js.add(lcd, buttonAndLCDLayout); // 配線穴 f3js.drawRectangle(x + width / 2 + 5, y + height - 35 - topmargin, 20, 10); f3js.drawRectangle(x + 15 + topmargin, y + height - 35 - topmargin, 20, 10); f3js.drawRectangle(x + width - 20 - topmargin, y + height - 35 - topmargin, 20, 10); // その他の面 var planes = rect.extrude(thickness); var margin = 5; planes[0].x = x; planes[0].y = y + height + margin; f3js.add(planes[0]); planes[1].x = x + width; planes[1].y = y + height + margin; f3js.add(planes[1]); planes[2].x = x + height; planes[2].y = y + height + thickness + margin; f3js.add(planes[2]); planes[3].x = x; planes[3].y = y + height + thickness + margin; f3js.add(planes[3]); planes[4].x = width; planes[4].y = 0; f3js.add(planes[4]); // 側面配線用穴 f3js.drawRectangle( x + 5, y + height + thickness * 2 + margin - 2 - 15, height - 10, 10); // IoTボード固定穴x4 (Arduino互換) f3js.drawCircle(x + 2 * width - 10 - 15.234, y + 20 + 2.539, 1.6); f3js.drawCircle(x + 2 * width - 10 - 13.965, y + 20 + 50.761, 1.6); f3js.drawCircle(x + 2 * width - 10 - 65.995, y + 20 + 17.774, 1.6); f3js.drawCircle(x + 2 * width - 10 - 65.995, y + 20 + 45.683, 1.6);
Customization

This content does not allow any customization.

Layout view options
Adding comments to variable declarations in the source code populate various GUI widgets in the "Customization" section depending on their types.

Design Alternatives

Modules

No modules are required for this project.

Layout
Program

Login first to use this feature.