UNPKG

6.84 kBJavaScriptView Raw
1var Board = require("./board");
2var Emitter = require("events").EventEmitter;
3var util = require("util");
4var Collection = require("./mixins/collection");
5
6var priv = new Map();
7var modes = {
8 INPUT: 0x00,
9 OUTPUT: 0x01,
10 ANALOG: 0x02,
11 PWM: 0x03,
12 SERVO: 0x04
13};
14
15/**
16 * Pin
17 * @constructor
18 *
19 * @description Direct Pin access objects
20 *
21 * @param {Object} opts Options: pin, freq, range
22 */
23
24function Pin(opts) {
25 if (!(this instanceof Pin)) {
26 return new Pin(opts);
27 }
28 if (opts === undefined || (typeof opts === "object" &&
29 opts.addr === undefined && opts.pin === undefined)) {
30 throw new Error("Pins must have a pin number");
31 }
32
33 var pinValue = typeof opts === "object" ? (opts.addr || opts.pin || 0) : opts;
34 var isAnalogInput = Pin.isAnalog(opts);
35 var isDTOA = false;
36
37 Board.Component.call(
38 this, opts = Board.Options(opts)
39 );
40
41 opts.addr = opts.addr || opts.pin;
42
43 if (this.io.analogPins.includes(pinValue)) {
44 isAnalogInput = false;
45 isDTOA = true;
46 }
47
48 var isPin = typeof opts !== "object";
49 var addr = isDTOA ? pinValue : (isPin ? opts : opts.addr);
50 var type = opts.type || (isAnalogInput ? "analog" : "digital");
51
52 // Create a private side table
53 var state = {
54 mode: null,
55 last: null,
56 value: 0
57 };
58
59 priv.set(this, state);
60
61 // Create read-only "addr(address)" property
62 Object.defineProperties(this, {
63 type: {
64 get: function() {
65 return type;
66 }
67 },
68 addr: {
69 get: function() {
70 return addr;
71 }
72 },
73 value: {
74 get: function() {
75 return state.value;
76 }
77 },
78 mode: {
79 set: function(mode) {
80 var state = priv.get(this);
81 state.mode = mode;
82 this.io.pinMode(this.addr, mode);
83 },
84 get: function() {
85 return priv.get(this).mode;
86 }
87 }
88 });
89
90 this.mode = typeof opts.as !== "undefined" ? opts.as :
91 (typeof opts.mode !== "undefined" ? opts.mode : (isAnalogInput ? 0x02 : 0x01));
92
93 this.freq = typeof opts.freq !== "undefined" ? opts.freq : 20;
94
95 if (this.mode === 0 || this.mode === 2) {
96 read(this);
97 }
98
99 if (type === "digital") {
100 Object.defineProperties(this, {
101 isHigh: {
102 get: function() {
103 return !!state.value;
104 }
105 },
106 isLow: {
107 get: function() {
108 return !state.value;
109 }
110 },
111 });
112 }
113}
114
115
116function read(pin) {
117 var state = priv.get(pin);
118
119 pin.io[pin.type + "Read"](pin.addr, function(data) {
120 state.value = data;
121 });
122
123 setInterval(function() {
124 var isNot, emit;
125
126 isNot = state.value ? "low" : "high";
127 emit = state.value ? "high" : "low";
128
129 if (state.mode === modes.INPUT) {
130 if (state.last === null) {
131 state.last = isNot;
132 }
133 if (state.last === isNot) {
134 state.last = emit;
135 pin.emit(emit, state.value);
136 pin.emit("change", state.value);
137 }
138 }
139 pin.emit("data", state.value);
140 }, pin.freq);
141}
142
143util.inherits(Pin, Emitter);
144
145/**
146 * Pin.@@MODE
147 *
148 * Read-only constants
149 * Pin.INPUT = 0x00
150 * Pin.OUTPUT = 0x01
151 * Pin.ANALOG = 0x02
152 * Pin.PWM = 0x03
153 * Pin.SERVO = 0x04
154 *
155 */
156Object.keys(modes).forEach(function(mode) {
157 Object.defineProperty(Pin, mode, {
158 value: modes[mode]
159 });
160});
161
162
163Pin.isAnalog = function(opts) {
164 if (typeof opts === "string" && Pin.isPrefixed(opts, ["I", "A"])) {
165 return true;
166 }
167
168 if (typeof opts === "object") {
169 return Pin.isAnalog(
170 typeof opts.addr !== "undefined" ? opts.addr : opts.pin
171 );
172 }
173};
174
175Pin.isPrefixed = function(value, prefixes) {
176 value = value[0];
177
178 return prefixes.reduce(function(resolution, prefix) {
179 if (!resolution) {
180 return prefix === value;
181 }
182 return resolution;
183 }, false);
184};
185
186Pin.write = function(pin, val) {
187 var state = priv.get(pin);
188
189 state.value = val;
190
191 // Set the correct mode (OUTPUT)
192 // This will only set if it needs to be set, otherwise a no-op
193 pin.mode = modes.OUTPUT;
194
195 // Create the correct type of write command
196 pin.io[pin.type + "Write"](pin.addr, val);
197
198 pin.emit("write", null, val);
199};
200
201Pin.read = function(pin, callback) {
202 // Set the correct mode (INPUT)
203 // This will only set if it needs to be set, otherwise a no-op
204
205 var isChanging = false;
206
207 if (pin.type === "digital" && pin.mode !== 0) {
208 isChanging = true;
209 pin.mode = modes.INPUT;
210 }
211
212 if (pin.type === "analog" && pin.mode !== 2) {
213 isChanging = true;
214 pin.mode = modes.ANALOG;
215 }
216
217 if (isChanging) {
218 read(pin);
219 }
220
221 pin.on("data", function() {
222 callback.call(pin, null, pin.value);
223 });
224};
225
226
227// Pin.prototype.isDigital = function() {
228// return this.addr > 1;
229// };
230
231// Pin.prototype.isAnalog = function() {
232// return this.board > 1;
233// };
234
235// Pin.prototype.isPWM = function() {
236// };
237
238// Pin.prototype.isServo = function() {
239// };
240
241// Pin.prototype.isI2C = function() {
242// };
243
244// Pin.prototype.isSerial = function() {
245// };
246
247// Pin.prototype.isInterrupt = function() {
248// };
249
250// Pin.prototype.isVersion = function() {
251// };
252
253
254Pin.prototype.query = function(callback) {
255 var index = this.addr;
256
257 if (this.type === "analog") {
258 index = this.io.analogPins[this.addr];
259 }
260
261 function handler() {
262 callback(this.io.pins[index]);
263 }
264
265 this.io.queryPinState(index, handler.bind(this));
266
267 return this;
268};
269
270/**
271 * high Write high/1 to the pin
272 * @return {Pin}
273 */
274
275Pin.prototype.high = function() {
276 var value = this.type === "analog" ? 255 : 1;
277 Pin.write(this, value);
278 this.emit("high");
279 return this;
280};
281
282/**
283 * low Write low/0 to the pin
284 * @return {Pin}
285 */
286
287Pin.prototype.low = function() {
288 Pin.write(this, 0);
289 this.emit("low");
290 return this;
291};
292
293/**
294 * read Read from the pin, value is passed to callback continuation
295 * @return {Pin}
296 */
297
298/**
299 * write Write to a pin
300 * @return {Pin}
301 */
302["read", "write"].forEach(function(operation) {
303 Pin.prototype[operation] = function(valOrCallback) {
304 Pin[operation](this, valOrCallback);
305 return this;
306 };
307});
308
309
310/**
311 * Pins()
312 * new Pins()
313 *
314 * Constructs an Array-like instance of all servos
315 */
316function Pins(numsOrObjects) {
317 if (!(this instanceof Pins)) {
318 return new Pins(numsOrObjects);
319 }
320
321 Object.defineProperty(this, "type", {
322 value: Pin
323 });
324
325 Collection.call(this, numsOrObjects);
326}
327
328util.inherits(Pins, Collection);
329
330[
331 "high", "low", "write"
332].forEach(function(method) {
333 Pins.prototype[method] = function() {
334 var length = this.length;
335
336 for (var i = 0; i < length; i++) {
337 this[i][method].apply(this[i], arguments);
338 }
339 return this;
340 };
341});
342
343
344// Assign Pins Collection class as static "method" of Pin.
345// TODO: Eliminate .Array for 1.0.0
346Pin.Array = Pins;
347Pin.Collection = Pins;
348
349/* istanbul ignore else */
350if (!!process.env.IS_TEST_MODE) {
351 Pin.purge = function() {
352 priv.clear();
353 };
354}
355
356module.exports = Pin;