Skip to content

Commit

Permalink
Added motion class with PIR controller
Browse files Browse the repository at this point in the history
  • Loading branch information
derekwheee committed May 9, 2015
1 parent 79b1207 commit 829a5a3
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ To interactively navigate the examples, visit the [Johnny-Five examples](http://
- [Proximity - HC-SR04](https://github.com/rwaldron/johnny-five/blob/master/docs/proximity-hcsr04.md)
- [Proximity - LIDAR-Lite](https://github.com/rwaldron/johnny-five/blob/master/docs/proximity-lidarlite.md)

### Motion
- [Motion](https://github.com/rwaldron/johnny-five/blob/master/docs/motion.md)

### Joystick
- [Joystick](https://github.com/rwaldron/johnny-five/blob/master/docs/joystick.md)
- [Joystick - Claw control](https://github.com/rwaldron/johnny-five/blob/master/docs/joystick-claw.md)
Expand Down
Binary file added docs/breadboard/motion.fzz
Binary file not shown.
Binary file added docs/breadboard/motion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions docs/motion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!--remove-start-->

# Motion


Basic infrared Motion example


Run with:
```bash
node eg/motion.js
```

<!--remove-end-->

```javascript
var five = require("johnny-five");
var board = new five.Board();

board.on("ready", function() {

// Create a new `motion` hardware instance.
var motion = new five.Motion(7);

// "calibrated" occurs once, at the beginning of a session,
motion.on("calibrated", function() {
console.log("calibrated");
});

// "motionstart" events are fired when the "calibrated"
// proximal area is disrupted, generally by some form of movement
motion.on("motionstart", function() {
console.log("motionstart");
});

// "motionend" events are fired following a "motionstart" event
// when no movement has occurred in X ms
motion.on("motionend", function() {
console.log("motionend");
});

// "data" events are fired at the interval set in opts.freq
// or every 25ms
motion.on("data", function(data) {
console.log(data);
});
});

```


## Illustrations / Photos


### Breadboard for "Motion"



![docs/breadboard/motion.png](breadboard/motion.png)<br>

Fritzing diagram: [docs/breadboard/motion.fzz](breadboard/motion.fzz)

&nbsp;





&nbsp;

<!--remove-start-->

## License
Copyright (c) 2012, 2013, 2014 Rick Waldron <waldron.rick@gmail.com>
Licensed under the MIT license.
Copyright (c) 2014, 2015 The Johnny-Five Contributors
Licensed under the MIT license.

<!--remove-end-->
31 changes: 31 additions & 0 deletions eg/motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
var five = require("../lib/johnny-five.js");
var board = new five.Board();

board.on("ready", function() {

// Create a new `motion` hardware instance.
var motion = new five.Motion(7);

// "calibrated" occurs once, at the beginning of a session,
motion.on("calibrated", function() {
console.log("calibrated");
});

// "motionstart" events are fired when the "calibrated"
// proximal area is disrupted, generally by some form of movement
motion.on("motionstart", function() {
console.log("motionstart");
});

// "motionend" events are fired following a "motionstart" event
// when no movement has occurred in X ms
motion.on("motionend", function() {
console.log("motionend");
});

// "data" events are fired at the interval set in opts.freq
// or every 25ms
motion.on("data", function(data) {
console.log(data);
});
});
1 change: 1 addition & 0 deletions lib/johnny-five.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
Led: require("./led"),
LedControl: require("./led/ledcontrol"),
Joystick: require("./joystick"),
Motion: require("./motion"),
Motor: require("./motor"),
Nodebot: require("./nodebot"),
Piezo: require("./piezo"),
Expand Down
135 changes: 135 additions & 0 deletions lib/motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
var Board = require("../lib/board.js"),
events = require("events"),
util = require("util"),
temporal = require("temporal"),
priv = new Map();

var Controllers = {
PIR: {
initialize: {
value: function (opts, dataHandler) {

var state = priv.get(this);
var calibrationDelay = "calibrationDelay" in opts ? opts.calibrationDelay : 30000;

this.io.pinMode(opts.pin, this.io.MODES.INPUT);

temporal.delay(calibrationDelay, function() {
state.isCalibrated = true;
this.emit("calibrated", null);
}.bind(this));

this.io.digitalRead(opts.pin, function(data) {
dataHandler.call(this, !!data);
}.bind(this));

}
}
},
};

/**
* Motion
* @constructor
*
* five.Motion(7);
*
* five.Motion({
* controller: "PIR",
* pin: 7,
* freq: 100,
* calibrationDelay: 1000
* });
*
*
* @param {Object} opts [description]
*
*/

function Motion(opts) {

if (!(this instanceof Motion)) {
return new Motion(opts);
}

var controller;
var freq = opts.freq || 25;

// new
var state;
var last = false;

Board.Device.call(
this, opts = Board.Options(opts)
);

if (typeof opts.controller === "string") {
controller = Controllers[opts.controller];
} else {
controller = opts.controller || Controllers["PIR"];
}

Object.defineProperties(this, controller);

state = {
detectedMotion: false,
isCalibrated: false
};

priv.set(this, state);

Object.defineProperties(this, {
/**
* [read-only] Current sensor state
* @property detectedMotion
* @type Boolean
*/
detectedMotion: {
get: function() {
return state.detectedMotion;
}
},
/**
* [read-only] Sensor calibration status
* @property isCalibrated
* @type Boolean
*/
isCalibrated: {
get: function() {
return state.isCalibrated;
}
},
});

if (typeof this.initialize === "function") {
this.initialize(opts, function(data) {
state.detectedMotion = data;
});
}

setInterval(function() {

var eventData = {
timestamp : Date.now(),
detectedMotion : state.detectedMotion,
isCalibrated : state.isCalibrated
};

if (state.isCalibrated && state.detectedMotion && !last) {
this.emit("motionstart", eventData);
}

if (state.isCalibrated && !state.detectedMotion && last) {
this.emit("motionend", eventData);
}

this.emit("data", eventData);

last = state.detectedMotion;

}.bind(this), freq);
}

util.inherits(Motion, events.EventEmitter);

module.exports = Motion;
98 changes: 98 additions & 0 deletions test/motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
var MockFirmata = require("./util/mock-firmata"),
five = require("../lib/johnny-five.js"),
events = require("events"),
sinon = require("sinon"),
Board = five.Board,
Motion = five.Motion,
board = new Board({
io: new MockFirmata(),
debug: false,
repl: false
});

exports["Motion"] = {
setUp: function(done) {
this.clock = sinon.useFakeTimers();
this.digitalRead = sinon.spy(board.io, "digitalRead");
this.motion = new Motion({
pin: 7,
calibrationDelay: 10,
board: board
});

this.instance = [{
name: "detectedMotion"
}, {
name: "isCalibrated"
}];

done();
},

tearDown: function(done) {
this.motion.removeAllListeners();
this.clock.restore();
this.digitalRead.restore();
done();
},

shape: function(test) {
test.expect(this.instance.length);

this.instance.forEach(function(property) {
test.notEqual(typeof this.motion[property.name], "undefined");
}, this);

test.done();
},

emitter: function(test) {
test.expect(1);

test.ok(this.motion instanceof events.EventEmitter);

test.done();
},

data: function(test) {
var spy = sinon.spy();
test.expect(1);
this.motion.on("data", spy);
this.clock.tick(25);
test.ok(spy.calledOnce);
test.done();
},

motionstart: function(test) {
var callback = this.digitalRead.args[0][1],
spy = sinon.spy();

test.expect(1);
this.motion.on("motionstart", spy);

// 0 then changes to 1
callback(0);
callback(1);
this.clock.tick(25);

test.ok(spy.calledOnce);
test.done();
},

motionend: function(test) {
var callback = this.digitalRead.args[0][1],
spy = sinon.spy();

test.expect(1);
this.motion.on("motionend", spy);

// 1 then changes to 0
callback(1);
this.clock.tick(25);
callback(0);
this.clock.tick(25);

test.ok(spy.calledOnce);
test.done();
}
};
11 changes: 11 additions & 0 deletions tpl/programs.json
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,17 @@
}
]
},
{
"topic": "Motion",
"classes": ["Motion"],
"examples": [
{
"file": "motion.js",
"title": "Motion",
"description": "Basic infrared Motion example"
}
]
},
{
"topic": "Joystick",
"classes": ["Joystick"],
Expand Down

0 comments on commit 829a5a3

Please sign in to comment.