UNPKG

johnny-five

Version:

The JavaScript Arduino Programming Framework.

753 lines (641 loc) 18.2 kB
var five = require("../lib/johnny-five.js"); var exec = require("child_process").exec; var temporal = require("temporal"); // var keypress = require("keypress"); var dualShock = require("dualshock-controller"); var argv = require("optimist").default({ noscan: false, calibrate: false }).argv; var board = new five.Board(); var controller = dualShock({ config: "dualShock3", analogStickSmoothing: true }); controller.isConnected = false; function scale(x, fromLow, fromHigh, toLow, toHigh) { return (x - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } function degrees(center, val) { if (val >= 128) { return scale(val, 128, 255, center - 1, 0); } return scale(val, 127, 0, center, 180); } // { pin: 2, id: "lf", startAt: 90 }, // { pin: 3, id: "la", startAt: 90 }, // { pin: 4, id: "lk", startAt: 90 }, // { pin: 5, id: "lt", startAt: 90 }, // { pin: 6, id: "lr", startAt: 90 }, // { pin: 7, id: "ly", startAt: 90 }, // { pin: 8, id: "rf", startAt: 90 }, // { pin: 9, id: "ra", startAt: 90 }, // { pin: 10, id: "rk", startAt: 90 }, // { pin: 11, id: "rt", startAt: 90 }, // { pin: 12, id: "rr", startAt: 90 }, // { pin: 13, id: "ry", startAt: 90 }, function move(positions, speed) { joints.forEach(function(part, i) { legs[part].to(positions[i], speed); }); } function still() { Object.keys(tasks).forEach(function(name) { if (tasks[name]) { tasks[name].stop(); } }); } var tasks = { controls: null, dance: null, turn: null, scanner: null, walk: null }; var legs = {}; var inits = [ { pin: 16, id: "rf", startAt: 90, isInverted: true }, { pin: 17, id: "ra", startAt: 90 }, { pin: 18, id: "rk", startAt: 90, isInverted: true }, { pin: 19, id: "rt", startAt: 90, isInverted: true }, { pin: 20, id: "rr", startAt: 90, isInverted: true }, { pin: 21, id: "ry", startAt: 90, isInverted: true }, { pin: 32, id: "lf", startAt: 90 }, { pin: 33, id: "la", startAt: 90 }, { pin: 34, id: "lk", startAt: 90 }, { pin: 35, id: "lt", startAt: 90 }, { pin: 36, id: "lr", startAt: 90 }, { pin: 37, id: "ly", startAt: 100 } ]; var joints = inits.map(function(init) { return init.id; }); var starts = inits.map(function(init) { return init.startAt; }); var gaits = [ { id: "rev", sets: [ { id: "short", isLoop: true, isInterruption: false, lapse: 500, sequence: [ // [ rf, rk, rh, lf, lk, lh ] // { moves: [ -7,-20,-20, 7, 20, 20 ] }, // { moves: [ 7,-20,-20, -7, 20, 20 ] }, // { moves: [ 7, 20, 20, -7,-20,-20 ] }, // { moves: [ -7, 20, 20, 7,-20,-20 ] } ] }, { id: "long", isLoop: true, isInterruption: false, lapse: 600, sequence: [ { moves: [-12,-35,-35, 12, 35, 35 ] }, { moves: [ 12,-35,-35,-12, 35, 35 ] }, { moves: [ 12, 35, 35,-12,-35,-35 ] }, { moves: [-12, 35, 35, 12,-35,-35 ] } ] } ] }, { id: "fwd", sets: [ { id: "short", isLoop: true, isInterruption: false, lapse: 500, sequence: [ // [ rf, rk, rh, lf, lk, lh ] { moves: [ 7,-20,-20, -7, 20, 20 ] }, { moves: [ -7,-20,-20, 7, 20, 20 ] }, { moves: [ -7, 20, 20, 7,-20,-20 ] }, { moves: [ 7, 20, 20, -7,-20,-20 ] } ] }, { id: "long", isLoop: true, isInterruption: false, lapse: 600, sequence: [ // [ rf, rk, rh, lf, lk, lh ] { moves: [ 12,-35,-35,-12, 35, 35 ] }, { moves: [-12,-35,-35, 12, 35, 35 ] }, { moves: [-12, 35, 35, 12,-35,-35 ] }, { moves: [ 12, 35, 35,-12,-35,-35 ] } ] } ] }, { id: "action", sets: [ { id: "rkick", isLoop: false, isInterruption: true, lapse: 500, sequence: [ // [ rf, rk, rh, lf, lk, lh ] { moves: [ 0, 0, 0, 0, 0, 0 ] }, { moves: [ 42, 0, 0,-14, 0, 0 ] }, { moves: [ 0,-32, 41,-23, 0, 0 ] }, { moves: [ 0, 24,-20,-23, 0, 0 ], lapse: 250 }, { moves: [ 0, 0, 0,-18, 0, 0 ] }, { moves: [ 0, 0, 0, 0, 0, 0 ] } ] }, { id: "lkick", isLoop: false, isInterruption: true, lapse: 500, sequence: [ // [ rf, rk, rh, lf, lk, lh ] { moves: [ 0, 0, 0, 0, 0, 0 ] }, { moves: [-14, 0, 0, 42, 0, 0 ] }, { moves: [-23, 0, 0, 0,-32, 41 ] }, { moves: [-23, 0, 0, 0, 24,-20 ], lapse: 250 }, { moves: [-18, 0, 0, 0, 0, 0 ] }, { moves: [ 0, 0, 0, 0, 0, 0 ] } ] } ] }, { id: "turn", sets: [ { id: "left", isLoop: false, isInterruption: true, lapse: 300, sequence: [ { moves: [ 0,-35,-40, 0, 35, 37 ] }, { moves: [ 0, 35, 37, 0,-35,-40 ] }, { moves: [-14, 35, 37, 20,-35,-40 ] }, { moves: [-14, 35, 37, 20, 0, 0 ] }, { moves: [ 20, 0, 0,-14, 0, 0 ] } ] }, { id: "right", isLoop: false, isInterruption: true, lapse: 300, sequence: [ { moves: [ 0, 35, 37, 0,-35,-40 ] }, { moves: [ 0,-35,-40, 0, 35, 37 ] }, { moves: [ 20,-35,-40,-14, 35, 37 ] }, { moves: [ 20, 0, 0,-14, 35, 37 ] }, { moves: [-14, 0, 0, 20, 0, 0 ] }, ] } ] }, { id: "correct", sets: [ { id: "left", isLoop: false, isInterruption: true, lapse: 250, sequence: [ // { moves: [ 0, 0, 0, 0, 0, 0 ] }, { moves: [ 35, 0, 0,-18, 0, 0 ] }, { moves: [], lapse: 100 }, { moves: [ 2,-20,-20,-18, 20, 20 ] }, { moves: [ 0,-20,-20, 0, 20, 20 ] }, { moves: [ 0, 0, 0, 0, 0, 0 ] }, ] }, { id: "right", isLoop: false, isInterruption: true, lapse: 250, sequence: [ // { moves: [ 0, 0, 0, 0, 0, 0 ] }, { moves: [-18, 0, 0, 35, 0, 0 ] }, { moves: [], lapse: 100 }, { moves: [-18, 20, 20, 2,-20,-20 ] }, { moves: [ 0, 20, 20, 0,-20,-20 ] }, { moves: [ 0, 0, 0, 0, 0, 0 ] }, ] } ] }, { id: "lean", sets: [ { id: "both", isLoop: false, isInterruption: true, lapse: 250, sequence: [ { moves: [ -15, 0, 0, 0, 0, 26, 0, 0, 0, 0 ] }, // { moves: [ 0, 0, 0, 0, 0, -20, 0, 0, 0, 0 ] }, // { moves: [-20, 0, 0, 0, 0, -16, 0, 0, 0, 0] } ] }, { id: "right", isLoop: false, isInterruption: true, lapse: 250, sequence: [ // { moves: [ 0, 0, 0, 0, 0, 0 ] }, { moves: [-18, 0, 0, 35, 0, 0 ] }, { moves: [], lapse: 100 }, { moves: [-18, 20, 20, 2,-20,-20 ] }, { moves: [ 0, 20, 20, 0,-20,-20 ] }, { moves: [ 0, 0, 0, 0, 0, 0 ] }, ] } ] }, { id: "home", sets: [ { id: "stand", isLoop: false, isInterruption: true, lapse: 500, sequence: [ { moves: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] } ] }, { id: "rest", isLoop: false, isInterruption: true, lapse: 500, sequence: [ { moves: [ 0, 50, 55, -24, 0, 0, 0, 50, 55, -24, 0, 0 ] } ] } ] } ]; board.on("ready", function() { legs = inits.reduce(function(accum, init) { accum[init.id] = new five.Servo(init); return accum; }, {}); var controls = gaits.reduce(function(controls, gait) { controls[gait.id] = gait.sets.reduce(function(seq, set) { // Create an API from gait.set // seq[set.id] = function(opts, callback) { var noLoop; if (typeof opts === "function") { callback = opts; opts = {}; } opts = opts || {}; noLoop = opts.noLoop; var hash = [gait.id, set.id].join(":"); // console.log( "set.isInterruption", set.isInterruption ); if (set.isInterruption) { isRightfirst = false; isWalking = false; isMoving = false; currently = null; } // Allow interruptions if (currently !== hash) { currently = hash; isMoving = false; } // If `isMoving` is currently `true`, bail out // to avoid pile up. if (isMoving) { return; } // If `isMoving` is currently `false`, // set it to `true` to avoid pile up isMoving = true; // still(); var queue = set.sequence.map(function(relative) { var lapse = set.lapse; var absolute; if (relative.lapse) { lapse = relative.lapse; } // Convert the relative movement values // to absolute servo positions. absolute = relative.moves.map(function(value, i) { return value + starts[i]; }); return { wait: lapse, task: function() { if (absolute.length) { move(absolute, lapse); } } } }); if (set.isLoop || opts.isLoop) { queue.push({ wait: 10, task: function() { isMoving = false; if (callback) { callback(set.id); } else { controls[gait.id][set.id](); } } }); } else { queue.push({ wait: 10, task: function() { isMoving = false; if (callback) { callback(set.id); } } }); } tasks.controls = temporal.queue(queue); }; return seq; }, {}); return controls; }, {}); var labels = [ "Foot", "Ankle", "Knee", "Thigh", "Hip Roll", "Hip Yaw" ]; var Group = { stash: { side: -1, index: 0 }, isGrouped: false, get side() { return Group.stash.side; }, set side(value) { // -1 = both, 0 = right, 1 = left var which = value === -1 ? "both" : (value ? "right" : "left"); console.log( "------- %s %s -------", which.toUpperCase(), labels[Group.stash.index].toUpperCase() ); Group.stash.side = value; }, get index() { return Group.stash.index; }, set index(value) { console.log( "Controlling %s", this.sets[value].name ); Group.stash.index = value; }, sets: [ { name: "Feet", // pins: [ 2, 8 ], parts: [ "lf", "rf" ], values: [ inits[6].startAt, inits[0].startAt ] }, { name: "Ankles", // pins: [ 3, 9 ], parts: [ "la", "ra" ], values: [ inits[7].startAt, inits[1].startAt ] }, { name: "Knees", // pins: [ 4, 10 ], parts: [ "lk", "rk" ], values: [ inits[8].startAt, inits[2].startAt ] }, { name: "Thighs", // pins: [ 5, 11 ], parts: [ "lt", "rt" ], values: [ inits[9].startAt, inits[3].startAt ] }, { name: "Hip Roll", // pins: [ 7, 13 ], parts: [ "lr", "rr" ], values: [ inits[10].startAt, inits[4].startAt ] }, { name: "Hip Yaw", // pins: [ 7, 13 ], parts: [ "ly", "ry" ], values: [ inits[11].startAt, inits[5].startAt ] } ], // { pin: 40, id: "rf", startAt: 89, isInverted: true }, // { pin: 41, id: "ra", startAt: 92, isInverted: true }, // { pin: 42, id: "rk", startAt: 86, isInverted: true }, // { pin: 43, id: "rt", startAt: 96, isInverted: true }, // { pin: 44, id: "rr", startAt: 90, isInverted: true }, // { pin: 45, id: "ry", startAt: 90, isInverted: true }, // { pin: 24, id: "lf", startAt: 95 }, // { pin: 25, id: "la", startAt: 88 }, // { pin: 26, id: "lk", startAt: 89 }, // { pin: 27, id: "lt", startAt: 90 }, // { pin: 28, id: "lr", startAt: 90 }, // { pin: 29, id: "ly", startAt: 100 } // { pin: 7, id: "rf", startAt: 89, isInverted: true }, // { pin: 8, id: "ra", startAt: 92, isInverted: true }, // { pin: 9, id: "rk", startAt: 86, isInverted: true }, // { pin: 10, id: "rt", startAt: 96, isInverted: true }, // { pin: 11, id: "rh", startAt: 90, isInverted: true }, // { pin: 2, id: "lf", startAt: 95 }, // { pin: 3, id: "la", startAt: 88 }, // { pin: 4, id: "lk", startAt: 89 }, // { pin: 5, id: "lt", startAt: 90 }, // { pin: 6, id: "lh", startAt: 100 } toData: function() { var right = []; var left = []; this.sets.forEach(function(set) { left.push(set.values[0]); right.push(set.values[1]); }); return right.concat(left); } }; // console.log( "" ); // console.log( "starts: ", starts ); // console.log( "toData: ", Group.toData() ); function tuner(step) { // Cannot control both at a time if (Group.side === -1) { return; } var set = Group.sets[Group.index]; var part = set.parts[Group.side]; var value = set.values[Group.side]; if (value === null) { value = legs[part].startAt; } if (step < 0) { value--; } else { value++; } process.nextTick(function() { legs[part].to(value); }); set.values[Group.side] = value; } function aMove(y, which) { var set = Group.sets[Group.index]; var part, pos; if (Group.isGrouped) { set.parts.forEach(function(part, i) { var pos = degrees(legs[part].startAt, y) | 0; legs[part].to(pos); set.values[i] = pos; }); } else { part = set.parts[which]; pos = degrees(legs[part].startAt, y) | 0; legs[part].to(pos); set.values[1] = pos; } } controller.on("connected", function() { console.log( "connected" ); // controller.isConnected = true; }); // controller.on("leftAnalogBump:press", function() { // left.isActivated = !left.isActivated; // }); // controller.on("rightAnalogBump:press", function() { // right.isActivated = !right.isActivated; // }); console.log( "argv.calibrate?", argv.calibrate ); // Calibration Mode // if (argv.calibrate) { controller.on("start:press", function() { var absolute = Group.toData(); var relative = absolute.map(function(value, i) { return value - starts[i]; }); console.log("---------------------------"); console.log("Absolute: ", Group.toData()); console.log("Relative: ", relative); }); controller.on("square:press", function() { var which = Group.side === -1 ? "both" : (Group.side ? "right" : "left"); var part = Group.sets[Group.index].name; var say = ["Controlling ", which, part].join(" "); console.log(say); exec("say " + say); }); controller.on("triangle:press", function() { labels[Group.stash.index] var values = Group.sets[Group.index].values; var which = labels[Group.stash.index].toLowerCase(); var say = ""; if (Group.side === -1) { // do both values.forEach(function(value, i) { var part = Group.sets[Group.index].parts[i]; var side = part[0] === "r" ? "right" : "left"; if (value === null) { value = "starting"; } say += [side, which, "is at", value, "degrees"].join(" ") + ", "; }); } else { // do side var part = Group.sets[Group.index].parts[Group.side] var value = Group.sets[Group.index].values[Group.side]; var side = part[0] === "r" ? "right" : "left"; if (value === null) { value = "starting"; } say = [side, which, "is at", value, "degrees"].join(" "); } exec("say " + say); }); controller.on("x:press", function() { Group.isGrouped = !Group.isGrouped; }); controller.on("circle:press", function() { Group.side = -1; }); controller.on("dpadLeft:press", function() { Group.side = 0; }); controller.on("dpadRight:press", function() { Group.side = 1; }); controller.on("dpadUp:press", function() { var next = Group.index + 1; if (next === Group.sets.length) { next = 0; } Group.index = next; }); controller.on("dpadDown:press", function() { var next = Group.index - 1; if (next === -1) { next = Group.sets.length - 1; } Group.index = next; }); ["left:move", "right:move"].forEach(function(type) { controller.on(type, function(position) { if (controller.isConnected) { aMove(position.y, type === "left:move" ? 0 : 1); } }); }); // controller.on("left:move", function(position) { // if (controller.isConnected) { // aMove(position.y); // } // }); // controller.on("right:move", function(position) { // if (controller.isConnected) { // aMove(position.y); // } // }); controller.on("l1:press", function() { tuner(-1); }); controller.on("r1:press", function() { tuner(1); }); } this.repl.inject({ rest: controls.home.rest, controls: controls, Group: Group, legs: legs, thighs: new five.Servos([legs.rt, legs.lt]), knees: new five.Servos([legs.rk, legs.lk]), ankles: new five.Servos([legs.ra, legs.la]), feet: new five.Servos([legs.rf, legs.lf]) }); process.nextTick(function() { controller.isConnected = true; controller.connect(); }); });