johnny-five
Version:
The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!
542 lines (486 loc) • 12.8 kB
JavaScript
const Board = require("./board");
const Collection = require("./mixins/collection");
const EVS = require("./evshield");
const Fn = require("./fn");
const Withinable = require("./mixins/withinable");
const Pins = Board.Pins;
const toFixed = Fn.toFixed;
const priv = new Map();
function analogHandler(options, callback) {
this.io.pinMode(this.pin, this.io.MODES.ANALOG);
this.io.analogRead(this.pin, data => {
callback.call(this, data);
});
}
const Controllers = {
GP2Y0A21YK: {
// https://www.sparkfun.com/products/242
initialize: {
value: analogHandler
},
toCm: {
value(value) {
return toFixed(12343.85 * (value ** -1.15), 2);
}
}
},
GP2D120XJ00F: {
// https://www.sparkfun.com/products/8959
initialize: {
value: analogHandler
},
toCm: {
value(value) {
return toFixed((2914 / (value + 5)) - 1, 2);
}
}
},
GP2Y0A02YK0F: {
// https://www.sparkfun.com/products/8958
// 15cm - 150cm
initialize: {
value: analogHandler
},
toCm: {
value(value) {
return toFixed(10650.08 * (value ** -0.935) - 10, 2);
}
}
},
GP2Y0A41SK0F: {
// https://www.sparkfun.com/products/12728
// 4cm - 30cm
initialize: {
value: analogHandler
},
toCm: {
value(value) {
return toFixed(2076 / (value - 11), 2);
}
}
},
GP2Y0A710K0F: {
// https://www.adafruit.com/products/1568
// 100cm - 500cm
initialize: {
value: analogHandler
},
toCm: {
value(value) {
return toFixed(3.8631e8 * (value ** -2.463343), 0);
}
}
},
SRF10: {
ADDRESSES: {
value: [0x70]
},
initialize: {
value(options, callback) {
const { Drivers } = require("./sip");
const address = Drivers.addressResolver(this, options);
const msUntilNextRead = 65;
// Set up I2C data connection
this.io.i2cConfig(options);
// Startup parameter
this.io.i2cWrite(address, [0x01, 0x10]);
this.io.i2cWrite(address, [0x02, 0xFF]);
const read = () => {
// 0x51 result in cm (centimeters)
this.io.i2cWrite(address, [0x00, 0x51]);
setTimeout(() => {
this.io.i2cWrite(address, [0x02]);
this.io.i2cReadOnce(address, 2, data => callback((data[0] << 8) | data[1]));
read();
}, msUntilNextRead);
};
read();
}
},
toCm: {
value(value) {
return value;
}
}
},
// LV-MaxSonar-EZ
// LV-MaxSonar-EZ0
// LV-MaxSonar-EZ1
MB1000: {
initialize: {
value: analogHandler
},
toCm: {
value(value) {
// From http://www.maxbotix.com/articles/032.htm
// ADC -> inches -> cm
//
//
// From intro in page 1
// 'The LV-MaxSonar-EZ detects objects
// from 0-inches to 254-inches (6.45-meters) and provides sonar range information from 6-
// inches out to 254-inches with 1-inch resolution.'
// 1inch = 2.54cm
return toFixed((value / 2) * 2.54, 2);
}
}
},
// HRLV-MaxSonar-EZ0
MB1003: {
initialize: {
value: analogHandler
},
toCm: {
value(value) {
// http://www.maxbotix.com/articles/032.htm
//
//
// From intro in page 1
// 'This sensor line features 1-mm resolution, .....'
return toFixed(value / 2, 1);
}
}
},
// XL-MaxSonar-EZ3
MB1230: {
initialize: {
value: analogHandler
},
toCm: {
value(value) {
// From http://www.maxbotix.com/articles/016.htm
// Using a Standard Range XL-MaxSonar with an ADC (Analog Digital Converter)
// When using a standard XL-MaxSonar with an ADC, verify that the sensor
// and micro-controller are referencing the same power supply and ground.
// This also assumes that the ADC being used is perfectly accurate.
// When reading the sensor's output with the scaling in centimeters with a
// 10-bit ADC, the range can be read directly off the ADC.
// If the ADC output reads 700 the range in centimeters is 700 centimeters.
//
// ADC -> cm
//
//
// From intro on page 1
// 'The MB1200 and MB1300 sensor series detects objects from 0-cm1
// to 765-cm (25.1 feet) or 1068cm (35 feet) (select models) and
// provide sonar range information from 20-cm2
// out to765-cm or 1068-cm (select models) with 1-cm resolution...'
return value >> 0;
}
}
},
HCSR04: {
initialize: {
value(options, callback) {
let pinValue = options.pinValue;
const msToNextRead = 65;
if (Pins.isFirmata(this)) {
if (typeof pinValue === "string" &&
(pinValue.length > 1 && pinValue[0] === "A")) {
pinValue = this.io.analogPins[+pinValue.slice(1)];
}
pinValue = +pinValue;
if (this.io.analogPins.includes(pinValue)) {
options.pin = pinValue;
}
this.pin = options.pin;
}
// Private settings object
const settings = {
pin: options.pin,
value: this.io.HIGH,
pulseOut: 5,
};
const read = () => {
this.io.pingRead(settings, microseconds => {
callback(microseconds);
setTimeout(read, msToNextRead);
});
};
read();
}
},
toCm: {
value(value) {
// https://www.sparkfun.com/products/13959
//
//
// From `Product features` paragraph at page 1
// 'Ultrasonic ranging module HC - SR04 provides 2cm - 400cm non-contact
// measurement function, the ranging accuracy can reach to 3mm'
return toFixed(value / 29.1 / 2, 1);
}
}
},
HCSR04I2CBACKPACK: {
ADDRESSES: {
value: [
0x27
]
},
initialize: {
value(options, callback) {
const { Drivers } = require("./sip");
const address = Drivers.addressResolver(this, options);
const msToNextRead = 90;
// set up IO connection:
this.io.i2cConfig(options);
if (typeof options.pin === "undefined") {
this.pin = 8;
}
const read = () => {
// Read the 2 data bytes from the "register" for the pin.
// When firmware is complete, update to:
// this.io.i2cReadOnce(address, this.pin, 2, function(data) {
this.io.i2cReadOnce(address, 2, data => {
callback((data[0] << 8) + data[1]);
setTimeout(read, msToNextRead);
});
};
read();
}
},
toCm: {
value(value) {
return toFixed(value / 29.1 / 2, 1);
}
}
},
LIDARLITE: {
ADDRESSES: {
value: [
0x62
]
},
REGISTER: {
value: {
ENABLE: 0x00,
READ: 0x8F,
}
},
initialize: {
value(options, callback) {
const { Drivers } = require("./sip");
const address = Drivers.addressResolver(this, options);
this.io.i2cConfig(options);
const read = () => {
this.io.i2cWrite(address, this.REGISTER.ENABLE, 0x04);
setTimeout(() => {
this.io.i2cReadOnce(address, this.REGISTER.READ, 2, bytes => {
// Step 5 of Quick Start Guide
callback((bytes[0] << 8) + bytes[1]);
read();
});
}, 20);
};
read();
}
},
toCm: {
value(value) {
//
// From `Technology` paragraph at page 11
// 'Our patented, high"accuracy"
// measurement"technique"enables"distance"measurement"accuracy down"to 1cm..'
return value >> 0;
}
}
},
EVS_EV3_IR: {
initialize: {
value(options, callback) {
const state = priv.get(this);
state.shield = EVS.shieldPort(options.pin);
state.ev3 = new EVS(Object.assign(options, {
io: this.io
}));
state.ev3.setup(state.shield, EVS.Type_EV3);
state.ev3.read(state.shield, EVS.Proximity, EVS.Proximity_Bytes, data => callback(data[0] | (data[1] << 8)));
}
},
toCm: {
value(value) {
return value;
}
}
},
EVS_EV3_US: {
initialize: {
value(options, callback) {
const state = priv.get(this);
state.shield = EVS.shieldPort(options.pin);
state.ev3 = new EVS(Object.assign(options, {
io: this.io
}));
state.ev3.setup(state.shield, EVS.Type_EV3);
state.ev3.read(state.shield, EVS.Proximity, EVS.Proximity_Bytes, data => callback(data[0] | (data[1] << 8)));
}
},
toCm: {
value(value) {
return value / 10;
}
}
},
};
// Sensor aliases
// IR
Controllers["2Y0A21"] = Controllers.GP2Y0A21YK;
Controllers["2D120X"] = Controllers.GP2D120XJ00F;
Controllers["2Y0A02"] = Controllers.GP2Y0A02YK0F;
Controllers["0A41"] = Controllers.GP2Y0A41SK0F;
Controllers["0A21"] = Controllers.GP2Y0A21YK;
Controllers["0A02"] = Controllers.GP2Y0A02YK0F;
Controllers["41SK0F"] = Controllers.GP2Y0A41SK0F;
Controllers["21YK"] = Controllers.GP2Y0A21YK;
Controllers["2YK0F"] = Controllers.GP2Y0A02YK0F;
// Sonar
Controllers.MB1010 = Controllers.MB1000;
Controllers["LV-MaxSonar-EZ"] = Controllers.MB1000;
Controllers["LV-MaxSonar-EZ0"] = Controllers.MB1000;
Controllers["LV-MaxSonar-EZ1"] = Controllers.MB1010;
Controllers["HRLV-MaxSonar-EZ0"] = Controllers.MB1003;
Controllers["XL-MaxSonar-EZ3"] = Controllers.MB1230;
// Ping
[
"HC-SR04",
"SR04",
"SRF05",
"SRF06",
"PARALLAXPING",
"SEEEDPING",
"GROVEPING",
"PING_PULSE_IN",
"ULTRASONIC_PING",
].forEach(alias => {
Controllers[alias] = Controllers.HCSR04;
});
// Ping/HCSR04 I2C Backpack
[
"HCSR04-I2C-BACKPACK",
"HC-SR04-I2C-BACKPACK",
"SR04-I2C-BACKPACK",
"SR04I2CBACKPACK",
"PINGI2CBACKPACK",
"PING-I2C-BACKPACK",
"HCSR04_I2C_BACKPACK",
"HC_SR04_I2C_BACKPACK",
"SR04_I2C_BACKPACK",
"SR04I2CBACKPACK",
"PINGI2CBACKPACK",
"PING_I2C_BACKPACK",
].forEach(alias => {
Controllers[alias] = Controllers.HCSR04I2CBACKPACK;
});
// LIDAR Lite
Controllers["LIDAR-Lite"] = Controllers.LIDARLITE;
Controllers.DEFAULT = Controllers["GP2Y0A21YK"];
/**
* Proximity
* @constructor
*
* five.Proximity("A0");
*
* five.Proximity({
* controller: "GP2Y0A41SK0F",
* pin: "A0",
* freq: 100
* });
*
*
* @param {Object} options [description]
*
*/
class Proximity extends Withinable {
constructor(options) {
super();
const pinValue = typeof options === "object" ? options.pin : options;
Board.Component.call(
this, options = Board.Options(options)
);
Board.Controller.call(this, Controllers, options);
const state = {};
const freq = options.freq || 25;
let raw = 0;
let last = 0;
if (!this.toCm) {
this.toCm = options.toCm || (x => x);
}
priv.set(this, state);
Object.defineProperties(this, {
/**
* [read-only] Calculated centimeter value
* @property centimeters
* @type Number
*/
centimeters: {
get() {
return this.toCm(raw);
}
},
cm: {
get() {
return this.centimeters;
}
},
/**
* [read-only] Calculated inch value
* @property inches
* @type Number
*/
inches: {
get() {
return toFixed(this.centimeters * 0.39, 2);
}
},
in: {
get() {
return this.inches;
}
},
});
if (typeof this.initialize === "function") {
options.pinValue = pinValue;
this.initialize(options, data => raw = data);
}
setInterval(() => {
if (raw === undefined) {
return;
}
const data = {
cm: this.cm,
centimeters: this.centimeters,
in: this.in,
inches: this.inches
};
this.emit("data", data);
if (raw !== last) {
last = raw;
this.emit("change", data);
}
}, freq);
}
}
/**
* new Proximity.Collection();
*/
Proximity.Collection = class extends Collection.Emitter {
constructor(numsOrObjects) {
super(numsOrObjects);
}
get type() {
return Proximity;
}
};
Collection.installMethodForwarding(
Proximity.Collection.prototype, Proximity.prototype
);
Proximity.Controllers = Object.keys(Controllers);
/* istanbul ignore else */
if (!!process.env.IS_TEST_MODE) {
Proximity.Controllers = Controllers;
Proximity.purge = () => {
priv.clear();
};
}
module.exports = Proximity;