1 | var util = require('util');
|
2 | var EventEmitter = require('events').EventEmitter;
|
3 |
|
4 | var PACKET_CONF = 0x55;
|
5 | var ACK_CONF = 0x33;
|
6 | var FIN_CONF = 0x16;
|
7 |
|
8 | var ACK_CMD = 0x00;
|
9 | var FIRMWARE_CMD = 0x01;
|
10 | var IR_TX_CMD = 0x02;
|
11 | var IR_RX_AVAIL_CMD = 0x03
|
12 | var IR_RX_CMD = 0x04;
|
13 | var RX_START_CMD = 0x05;
|
14 | var RX_STOP_CMD = 0x06;
|
15 | var MAX_SIGNAL_DURATION = 200;
|
16 |
|
17 | var Infrared = function(hardware, callback) {
|
18 |
|
19 | this.spi = hardware.SPI({clockSpeed : 1000, mode:2});
|
20 | this.chipSelect = hardware.gpio(1);
|
21 | this.reset = hardware.gpio(2);
|
22 | this.irq = hardware.gpio(3);
|
23 | this.transmitting = false;
|
24 | this.listening = false;
|
25 | this.chipSelect.output().high();
|
26 | this.reset.output().high();
|
27 | this.irq.output().low();
|
28 |
|
29 | var self = this;
|
30 |
|
31 |
|
32 | this.on('newListener', function(event) {
|
33 |
|
34 | if (event == "length" && !this.listeners(event).length) {
|
35 | self.setListening(1);
|
36 | }
|
37 | });
|
38 |
|
39 | this.on('removeListener', function(event) {
|
40 |
|
41 | if (event == "length" && !this.listeners(event).length) {
|
42 | self.setListening(0);
|
43 | }
|
44 | });
|
45 |
|
46 |
|
47 | this.establishCommunication(3, function(err, version) {
|
48 |
|
49 | if (err) {
|
50 | setImmediate(function() {
|
51 | self.emit('error', err);
|
52 | });
|
53 | }
|
54 | else {
|
55 | setImmediate(function() {
|
56 | self.emit('ready');
|
57 | });
|
58 | }
|
59 |
|
60 | self.setListening(0);
|
61 |
|
62 |
|
63 | self.irq.watch('rise', self.IRQHandler.bind(self));
|
64 |
|
65 |
|
66 | callback && callback(err, self);
|
67 | });
|
68 | }
|
69 |
|
70 | util.inherits(Infrared, EventEmitter);
|
71 |
|
72 | Infrared.prototype.IRQHandler = function() {
|
73 |
|
74 | if (!this.transmitting) {
|
75 |
|
76 | this.getRXDurationsLength();
|
77 |
|
78 | } else {
|
79 |
|
80 |
|
81 | setTimeout(this.IRQHandler.bind(this), 1000);
|
82 | }
|
83 | }
|
84 |
|
85 | Infrared.prototype.setListening = function(set, callback) {
|
86 | var self = this;
|
87 |
|
88 | var cmd = set ? RX_START_CMD : RX_STOP_CMD;
|
89 |
|
90 | var response = this.SPITransfer([cmd, 0x00, 0x00]);
|
91 |
|
92 | self.validateResponse(response, [PACKET_CONF, cmd], function(valid) {
|
93 |
|
94 | if (!valid) {
|
95 | return callback && callback(new Error("Invalid response on setting rx on/off."));
|
96 | }
|
97 | self.listening = set ? true : false;
|
98 |
|
99 | callback && callback();
|
100 | })
|
101 | }
|
102 |
|
103 | Infrared.prototype.getRXDurationsLength = function() {
|
104 | var self = this;
|
105 |
|
106 |
|
107 |
|
108 | this.SPITransfer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00], function(response) {
|
109 |
|
110 |
|
111 | self.validateResponse(response, [PACKET_CONF, IR_RX_AVAIL_CMD, 1], function(valid) {
|
112 | if (valid) {
|
113 | var numInt16 = response[3];
|
114 | self.emit('length', numInt16);
|
115 | }
|
116 | });
|
117 | });
|
118 | }
|
119 |
|
120 | Infrared.prototype.fetchRXDurations = function() {
|
121 | var self = this;
|
122 |
|
123 |
|
124 |
|
125 | this.SPITransfer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00], function(response) {
|
126 |
|
127 |
|
128 | self.validateResponse(response, [PACKET_CONF, IR_RX_AVAIL_CMD, 1], function(valid) {
|
129 | if (valid) {
|
130 | var numInt16 = response[3];
|
131 |
|
132 |
|
133 | var numBytes = numInt16 * 2;
|
134 |
|
135 | var rxHeader = [IR_RX_CMD, 0x00, 0x00];
|
136 | var packet = rxHeader.concat(EmptyArray(numBytes))
|
137 |
|
138 |
|
139 |
|
140 | packet.push(FIN_CONF);
|
141 |
|
142 | self.SPITransfer(packet, function(response) {
|
143 |
|
144 | var fin = response[response.length-1];
|
145 |
|
146 | if (fin != FIN_CONF) {
|
147 | console.log("Warning: Received Packet Out of Frame.");
|
148 | return;
|
149 | }
|
150 |
|
151 | if (err){
|
152 | return;
|
153 | }
|
154 |
|
155 | else {
|
156 |
|
157 | var arr = response.slice(rxHeader.length, response.length);
|
158 |
|
159 |
|
160 | self.emit('data', arr);
|
161 | }
|
162 | })
|
163 | }
|
164 | })
|
165 | })
|
166 | }
|
167 |
|
168 |
|
169 | function EmptyArray(size) {
|
170 | var arr = [];
|
171 |
|
172 | for (var i = 0; i < size; i++) {
|
173 | arr[i] = 0;
|
174 | }
|
175 |
|
176 | return arr;
|
177 | }
|
178 |
|
179 | Infrared.prototype.sendRawSignal = function(frequency, signalDurations, callback) {
|
180 | if (frequency <= 0) {
|
181 | setImmediate(function() {
|
182 | callback && callback(new Error("Invalid frequency. Must be greater than zero. Works best between 36-40."));
|
183 | });
|
184 | }
|
185 | else if (signalDurations.length > MAX_SIGNAL_DURATION) {
|
186 | setImmediate(function() {
|
187 | callback && callback(new Error("Invalid buffer length. Must be between 1 and ", MAX_SIGNAL_DURATION));
|
188 | })
|
189 | } else {
|
190 |
|
191 | this.transmitting = true;
|
192 |
|
193 | var self = this;
|
194 |
|
195 |
|
196 | var tx = this.constructTXPacket(frequency, signalDurations);
|
197 |
|
198 |
|
199 | this.SPITransfer(tx, function(response) {
|
200 |
|
201 | self.transmitting = false;
|
202 |
|
203 |
|
204 | if (err) {
|
205 | setImmediate(function() {
|
206 | callback && callback(err);
|
207 | });
|
208 | return;
|
209 | }
|
210 |
|
211 | else if (!self.validateResponse(response, [PACKET_CONF, IR_TX_CMD, frequency, signalDurations.length/2])) {
|
212 | err = new Error("Invalid response from raw signal packet: " + response);
|
213 | }
|
214 |
|
215 | setImmediate(function() {
|
216 | callback && callback(err);
|
217 | });
|
218 | });
|
219 | }
|
220 | }
|
221 |
|
222 | Infrared.prototype.constructTXPacket = function(frequency, signalDurations) {
|
223 |
|
224 | var tx = [];
|
225 |
|
226 | tx.push(IR_TX_CMD);
|
227 |
|
228 | tx.push(frequency);
|
229 |
|
230 |
|
231 | tx.push(signalDurations.length/2)
|
232 |
|
233 |
|
234 | for (var i = 0; i < signalDurations.length; i++) {
|
235 |
|
236 | tx.push(signalDurations.readUInt8(i));
|
237 | }
|
238 |
|
239 | tx.push(0x00);
|
240 |
|
241 |
|
242 | tx.push(FIN_CONF);
|
243 |
|
244 |
|
245 | return tx;
|
246 | }
|
247 |
|
248 | Infrared.prototype.getFirmwareVersion = function(callback) {
|
249 |
|
250 | var self = this;
|
251 | this.SPITransfer([FIRMWARE_CMD, 0x00, 0x00], function(response) {
|
252 |
|
253 | if (err) return callback(err, null);
|
254 | if (self.validateResponse(response, [PACKET_CONF]) && response.length == 3) {
|
255 | setImmediate(function() {
|
256 | callback && callback(null, response[2]);
|
257 | });
|
258 | } else {
|
259 | setImmediate(function() {
|
260 | callback && callback(new Error("Error retrieving Firmware Version"));
|
261 | });
|
262 | }
|
263 | });
|
264 | }
|
265 |
|
266 | Infrared.prototype.establishCommunication = function(retries, callback){
|
267 | var response;
|
268 | while (retries) {
|
269 | response = this.SPITransfer([FIRMWARE_CMD, 0x00, 0x00]);
|
270 | var valid = this.validateResponse(response, [PACKET_CONF, FIRMWARE_CMD]);
|
271 | if (valid) {
|
272 | this.connected = true;
|
273 | callback && callback(null, response[2]);
|
274 | break;
|
275 | } else {
|
276 | retries--;
|
277 | if (!retries) {
|
278 | callback && callback(new Error("Can't connect with module..."));
|
279 | break;
|
280 | }
|
281 | }
|
282 | }
|
283 | }
|
284 |
|
285 | Infrared.prototype.validateResponse = function(values, expected, callback) {
|
286 | var res = false;
|
287 |
|
288 |
|
289 | for (var i = 0; i < values.length; i++) {
|
290 | var value = values[i];
|
291 | if (value != 0 && value != 255) {
|
292 | res = true;
|
293 | }
|
294 | }
|
295 |
|
296 | setImmediate(function() {
|
297 | callback && callback(res);
|
298 | })
|
299 |
|
300 | return res;
|
301 | }
|
302 |
|
303 | Infrared.prototype.SPITransfer = function(data, callback) {
|
304 |
|
305 |
|
306 | data = new Buffer(data);
|
307 |
|
308 | this.chipSelect.low();
|
309 |
|
310 | var ret = this.spi.transferSync(data);
|
311 |
|
312 | this.chipSelect.high();
|
313 |
|
314 |
|
315 | callback && callback(ret);
|
316 |
|
317 |
|
318 | return ret;
|
319 | }
|
320 |
|
321 | exports.Infrared = Infrared;
|
322 | exports.use = function (hardware, callback) {
|
323 | return new Infrared(hardware, callback);
|
324 | }; |
\ | No newline at end of file |