// 各種ドライバ
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);