UNPKG

9.23 kBJavaScriptView Raw
1var util = require('util');
2var EventEmitter = require('events').EventEmitter;
3
4var PACKET_CONF = 0x55;
5var ACK_CONF = 0x33;
6var FIN_CONF = 0x16;
7
8var ACK_CMD = 0x00;
9var FIRMWARE_CMD = 0x01;
10var IR_TX_CMD = 0x02;
11var IR_RX_AVAIL_CMD = 0x03
12var IR_RX_CMD = 0x04;
13var RX_START_CMD = 0x05;
14var RX_STOP_CMD = 0x06;
15var MAX_SIGNAL_DURATION = 200;
16
17var 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 // If we get a new listener
32 this.on('newListener', function(event) {
33 // And they are listening for rx data and we haven't been yet
34 if (event == "length" && !this.listeners(event).length) {
35 self.setListening(1);
36 }
37 });
38
39 this.on('removeListener', function(event) {
40 // If this was for the rx data event and there aren't any more listeners
41 if (event == "length" && !this.listeners(event).length) {
42 self.setListening(0);
43 }
44 });
45
46 // Make sure we can communicate with the module
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 // Make sure we aren't gathering rx data until someone is listening.
60 self.setListening(0);
61
62 // Start listening for IRQ interrupts
63 self.irq.watch('rise', self.IRQHandler.bind(self));
64
65 // Complete the setup
66 callback && callback(err, self);
67 });
68}
69
70util.inherits(Infrared, EventEmitter);
71
72Infrared.prototype.IRQHandler = function() {
73 // If we are not in the middle of transmitting
74 if (!this.transmitting) {
75 // Receive the durations
76 this.getRXDurationsLength();
77 // this.fetchRXDurations();
78 } else {
79 // If we are, check back in 2 seconds
80 // TODO: reduce this timeout when we're running faster
81 setTimeout(this.IRQHandler.bind(this), 1000);
82 }
83}
84
85Infrared.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
103Infrared.prototype.getRXDurationsLength = function() {
104 var self = this;
105 // We have to pull chip select high in case we were in the middle of something else
106
107
108 this.SPITransfer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00], function(response) {
109 // DO something smarter than this eventually
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
120Infrared.prototype.fetchRXDurations = function() {
121 var self = this;
122 // We have to pull chip select high in case we were in the middle of something else
123
124 // this.chipSelect.high();
125 this.SPITransfer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00], function(response) {
126 // DO something smarter than this eventually
127
128 self.validateResponse(response, [PACKET_CONF, IR_RX_AVAIL_CMD, 1], function(valid) {
129 if (valid) {
130 var numInt16 = response[3];
131
132 // (We have two bytes per element...);
133 var numBytes = numInt16 * 2;
134
135 var rxHeader = [IR_RX_CMD, 0x00, 0x00];
136 var packet = rxHeader.concat(EmptyArray(numBytes))
137
138
139 // Push the stop bit on there.
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;// console.log("Issue sending dummy bytes...");
153 }
154
155 else {
156 // Remove the header echoes at the beginning
157 var arr = response.slice(rxHeader.length, response.length);
158
159 // Emit the buffer
160 self.emit('data', arr);
161 }
162 })
163 }
164 })
165 })
166}
167
168// Remove once Array class works...
169function 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
179Infrared.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 // Make the packet
196 var tx = this.constructTXPacket(frequency, signalDurations);
197
198 // Send it over
199 this.SPITransfer(tx, function(response) {
200
201 self.transmitting = false;
202
203 // If there was an error already, set immediate on the callback
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
222Infrared.prototype.constructTXPacket = function(frequency, signalDurations) {
223 // Create array
224 var tx = [];
225 // Add command
226 tx.push(IR_TX_CMD);
227 // Frequency of PWN
228 tx.push(frequency);
229
230 // Add length of signal durations in terms of int16s
231 tx.push(signalDurations.length/2)
232
233 // For each signal duration
234 for (var i = 0; i < signalDurations.length; i++) {
235 // Send upper and lower bits
236 tx.push(signalDurations.readUInt8(i));
237 }
238 // Put a dummy bit to get the last echo
239 tx.push(0x00);
240
241 // Put the finish confirmation
242 tx.push(FIN_CONF);
243
244 // return
245 return tx;
246}
247
248Infrared.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
266Infrared.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
285Infrared.prototype.validateResponse = function(values, expected, callback) {
286 var res = false;
287
288 // TODO: Replace with the 'every' method
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
303Infrared.prototype.SPITransfer = function(data, callback) {
304
305 // Pull Chip select down prior to transfer
306 data = new Buffer(data);
307
308 this.chipSelect.low();
309 // Send over the data
310 var ret = this.spi.transferSync(data);
311 // Pull chip select back up
312 this.chipSelect.high();
313
314 // Call any callbacks
315 callback && callback(ret);
316
317 // Return the data
318 return ret;
319}
320
321exports.Infrared = Infrared;
322exports.use = function (hardware, callback) {
323 return new Infrared(hardware, callback);
324};
\No newline at end of file