|
@@ -0,0 +1,306 @@
|
|
1
|
+(function() {
|
|
2
|
+ 'use strict';
|
|
3
|
+ var ArduinoFirmata, SerialPort, debug, events, exports, serialport,
|
|
4
|
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
|
5
|
+ hasProp = {}.hasOwnProperty;
|
|
6
|
+
|
|
7
|
+ events = require('eventemitter2');
|
|
8
|
+
|
|
9
|
+ SerialPort = (serialport = require('serialport')).SerialPort;
|
|
10
|
+
|
|
11
|
+ debug = require('debug')('arduino-firmata');
|
|
12
|
+
|
|
13
|
+ exports = module.exports = ArduinoFirmata = (function(superClass) {
|
|
14
|
+ extend(ArduinoFirmata, superClass);
|
|
15
|
+
|
|
16
|
+ ArduinoFirmata.Status = {
|
|
17
|
+ CLOSE: 0,
|
|
18
|
+ OPEN: 1
|
|
19
|
+ };
|
|
20
|
+
|
|
21
|
+ ArduinoFirmata.INPUT = 0;
|
|
22
|
+
|
|
23
|
+ ArduinoFirmata.OUTPUT = 1;
|
|
24
|
+
|
|
25
|
+ ArduinoFirmata.ANALOG = 2;
|
|
26
|
+
|
|
27
|
+ ArduinoFirmata.PWM = 3;
|
|
28
|
+
|
|
29
|
+ ArduinoFirmata.SERVO = 4;
|
|
30
|
+
|
|
31
|
+ ArduinoFirmata.SHIFT = 5;
|
|
32
|
+
|
|
33
|
+ ArduinoFirmata.I2C = 6;
|
|
34
|
+
|
|
35
|
+ ArduinoFirmata.LOW = 0;
|
|
36
|
+
|
|
37
|
+ ArduinoFirmata.HIGH = 1;
|
|
38
|
+
|
|
39
|
+ ArduinoFirmata.MAX_DATA_BYTES = 32;
|
|
40
|
+
|
|
41
|
+ ArduinoFirmata.DIGITAL_MESSAGE = 0x90;
|
|
42
|
+
|
|
43
|
+ ArduinoFirmata.ANALOG_MESSAGE = 0xE0;
|
|
44
|
+
|
|
45
|
+ ArduinoFirmata.REPORT_ANALOG = 0xC0;
|
|
46
|
+
|
|
47
|
+ ArduinoFirmata.REPORT_DIGITAL = 0xD0;
|
|
48
|
+
|
|
49
|
+ ArduinoFirmata.SET_PIN_MODE = 0xF4;
|
|
50
|
+
|
|
51
|
+ ArduinoFirmata.REPORT_VERSION = 0xF9;
|
|
52
|
+
|
|
53
|
+ ArduinoFirmata.SYSTEM_RESET = 0xFF;
|
|
54
|
+
|
|
55
|
+ ArduinoFirmata.START_SYSEX = 0xF0;
|
|
56
|
+
|
|
57
|
+ ArduinoFirmata.END_SYSEX = 0xF7;
|
|
58
|
+
|
|
59
|
+ ArduinoFirmata.list = function(callback) {
|
|
60
|
+ return serialport.list(function(err, ports) {
|
|
61
|
+ var devices, j, len, port;
|
|
62
|
+ if (err) {
|
|
63
|
+ return callback(err);
|
|
64
|
+ }
|
|
65
|
+ devices = [];
|
|
66
|
+ for (j = 0, len = ports.length; j < len; j++) {
|
|
67
|
+ port = ports[j];
|
|
68
|
+ if (/usb|acm|com\d+/i.test(port.comName)) {
|
|
69
|
+ devices.push(port.comName);
|
|
70
|
+ }
|
|
71
|
+ }
|
|
72
|
+ return callback(null, devices);
|
|
73
|
+ });
|
|
74
|
+ };
|
|
75
|
+
|
|
76
|
+ function ArduinoFirmata() {
|
|
77
|
+ this.status = ArduinoFirmata.Status.CLOSE;
|
|
78
|
+ this.wait_for_data = 0;
|
|
79
|
+ this.execute_multi_byte_command = 0;
|
|
80
|
+ this.multi_byte_channel = 0;
|
|
81
|
+ this.stored_input_data = [];
|
|
82
|
+ this.parsing_sysex = false;
|
|
83
|
+ this.sysex_bytes_read = 0;
|
|
84
|
+ this.digital_output_data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
85
|
+ this.digital_input_data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
86
|
+ this.analog_input_data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
87
|
+ this.boardVersion = null;
|
|
88
|
+ }
|
|
89
|
+
|
|
90
|
+ ArduinoFirmata.prototype.isOldArduinoDevice = function() {
|
|
91
|
+ return /usbserial|USB/.test(this.serialport_name);
|
|
92
|
+ };
|
|
93
|
+
|
|
94
|
+ ArduinoFirmata.prototype.connect = function(serialport_name, opts) {
|
|
95
|
+ this.serialport_name = serialport_name;
|
|
96
|
+ if (opts == null) {
|
|
97
|
+ opts = {
|
|
98
|
+ baudrate: 57600
|
|
99
|
+ };
|
|
100
|
+ }
|
|
101
|
+ opts.parser = serialport.parsers.raw;
|
|
102
|
+ if (!this.serialport_name) {
|
|
103
|
+ ArduinoFirmata.list((function(_this) {
|
|
104
|
+ return function(err, devices) {
|
|
105
|
+ return _this.connect(devices[0], opts);
|
|
106
|
+ };
|
|
107
|
+ })(this));
|
|
108
|
+ return this;
|
|
109
|
+ }
|
|
110
|
+ this.once('boardReady', function() {
|
|
111
|
+ var io_init_wait;
|
|
112
|
+ debug('boardReady');
|
|
113
|
+ io_init_wait = this.isOldArduinoDevice() ? (debug("old arduino device found " + this.serialport_name), 3000) : (debug("new arduino device found " + this.serialport_name), 100);
|
|
114
|
+ debug("wait " + io_init_wait + "(msec)");
|
|
115
|
+ return setTimeout((function(_this) {
|
|
116
|
+ return function() {
|
|
117
|
+ var i, j, k;
|
|
118
|
+ for (i = j = 0; j < 6; i = ++j) {
|
|
119
|
+ _this.write([ArduinoFirmata.REPORT_ANALOG | i, 1]);
|
|
120
|
+ }
|
|
121
|
+ for (i = k = 0; k < 2; i = ++k) {
|
|
122
|
+ _this.write([ArduinoFirmata.REPORT_DIGITAL | i, 1]);
|
|
123
|
+ }
|
|
124
|
+ debug('init IO ports');
|
|
125
|
+ return _this.emit('connect');
|
|
126
|
+ };
|
|
127
|
+ })(this), io_init_wait);
|
|
128
|
+ });
|
|
129
|
+ this.serialport = new SerialPort(this.serialport_name, opts);
|
|
130
|
+ this.serialport.once('open', (function(_this) {
|
|
131
|
+ return function() {
|
|
132
|
+ var cid;
|
|
133
|
+ cid = setInterval(function() {
|
|
134
|
+ debug('request REPORT_VERSION');
|
|
135
|
+ return _this.write([ArduinoFirmata.REPORT_VERSION]);
|
|
136
|
+ }, 500);
|
|
137
|
+ _this.once('boardVersion', function(version) {
|
|
138
|
+ clearInterval(cid);
|
|
139
|
+ _this.status = ArduinoFirmata.Status.OPEN;
|
|
140
|
+ return _this.emit('boardReady');
|
|
141
|
+ });
|
|
142
|
+ return _this.serialport.on('data', function(data) {
|
|
143
|
+ var byte, j, len, results;
|
|
144
|
+ results = [];
|
|
145
|
+ for (j = 0, len = data.length; j < len; j++) {
|
|
146
|
+ byte = data[j];
|
|
147
|
+ results.push(_this.process_input(byte));
|
|
148
|
+ }
|
|
149
|
+ return results;
|
|
150
|
+ });
|
|
151
|
+ };
|
|
152
|
+ })(this));
|
|
153
|
+ return this;
|
|
154
|
+ };
|
|
155
|
+
|
|
156
|
+ ArduinoFirmata.prototype.isOpen = function() {
|
|
157
|
+ return this.status === ArduinoFirmata.Status.OPEN;
|
|
158
|
+ };
|
|
159
|
+
|
|
160
|
+ ArduinoFirmata.prototype.close = function(callback) {
|
|
161
|
+ this.status = ArduinoFirmata.Status.CLOSE;
|
|
162
|
+ return this.serialport.close(callback);
|
|
163
|
+ };
|
|
164
|
+
|
|
165
|
+ ArduinoFirmata.prototype.reset = function(callback) {
|
|
166
|
+ return this.write([ArduinoFirmata.SYSTEM_RESET], callback);
|
|
167
|
+ };
|
|
168
|
+
|
|
169
|
+ ArduinoFirmata.prototype.write = function(bytes, callback) {
|
|
170
|
+ return this.serialport.write(bytes, callback);
|
|
171
|
+ };
|
|
172
|
+
|
|
173
|
+ ArduinoFirmata.prototype.sysex = function(command, data, callback) {
|
|
174
|
+ var write_data;
|
|
175
|
+ if (data == null) {
|
|
176
|
+ data = [];
|
|
177
|
+ }
|
|
178
|
+ data = data.map(function(i) {
|
|
179
|
+ return i & 0x7f;
|
|
180
|
+ });
|
|
181
|
+ write_data = [ArduinoFirmata.START_SYSEX, command].concat(data, [ArduinoFirmata.END_SYSEX]);
|
|
182
|
+ return this.write(write_data, callback);
|
|
183
|
+ };
|
|
184
|
+
|
|
185
|
+ ArduinoFirmata.prototype.pinMode = function(pin, mode, callback) {
|
|
186
|
+ switch (mode) {
|
|
187
|
+ case true:
|
|
188
|
+ mode = ArduinoFirmata.OUTPUT;
|
|
189
|
+ break;
|
|
190
|
+ case false:
|
|
191
|
+ mode = ArduinoFirmata.INPUT;
|
|
192
|
+ }
|
|
193
|
+ return this.write([ArduinoFirmata.SET_PIN_MODE, pin, mode], callback);
|
|
194
|
+ };
|
|
195
|
+
|
|
196
|
+ ArduinoFirmata.prototype.digitalWrite = function(pin, value, callback) {
|
|
197
|
+ var port_num;
|
|
198
|
+ this.pinMode(pin, ArduinoFirmata.OUTPUT);
|
|
199
|
+ port_num = (pin >>> 3) & 0x0F;
|
|
200
|
+ if (value === 0 || value === false) {
|
|
201
|
+ this.digital_output_data[port_num] &= ~(1 << (pin & 0x07));
|
|
202
|
+ } else {
|
|
203
|
+ this.digital_output_data[port_num] |= 1 << (pin & 0x07);
|
|
204
|
+ }
|
|
205
|
+ return this.write([ArduinoFirmata.DIGITAL_MESSAGE | port_num, this.digital_output_data[port_num] & 0x7F, this.digital_output_data[port_num] >>> 7], callback);
|
|
206
|
+ };
|
|
207
|
+
|
|
208
|
+ ArduinoFirmata.prototype.analogWrite = function(pin, value, callback) {
|
|
209
|
+ value = Math.floor(value);
|
|
210
|
+ this.pinMode(pin, ArduinoFirmata.PWM);
|
|
211
|
+ return this.write([ArduinoFirmata.ANALOG_MESSAGE | (pin & 0x0F), value & 0x7F, value >>> 7], callback);
|
|
212
|
+ };
|
|
213
|
+
|
|
214
|
+ ArduinoFirmata.prototype.servoWrite = function(pin, angle, callback) {
|
|
215
|
+ this.pinMode(pin, ArduinoFirmata.SERVO);
|
|
216
|
+ return this.write([ArduinoFirmata.ANALOG_MESSAGE | (pin & 0x0F), angle & 0x7F, angle >>> 7], callback);
|
|
217
|
+ };
|
|
218
|
+
|
|
219
|
+ ArduinoFirmata.prototype.digitalRead = function(pin) {
|
|
220
|
+ return ((this.digital_input_data[pin >>> 3] >>> (pin & 0x07)) & 0x01) > 0;
|
|
221
|
+ };
|
|
222
|
+
|
|
223
|
+ ArduinoFirmata.prototype.analogRead = function(pin) {
|
|
224
|
+ return this.analog_input_data[pin];
|
|
225
|
+ };
|
|
226
|
+
|
|
227
|
+ ArduinoFirmata.prototype.process_input = function(input_data) {
|
|
228
|
+ var analog_value, command, diff, i, j, old_analog_value, results, stat, sysex_command, sysex_data;
|
|
229
|
+ if (this.parsing_sysex) {
|
|
230
|
+ if (input_data === ArduinoFirmata.END_SYSEX) {
|
|
231
|
+ this.parsing_sysex = false;
|
|
232
|
+ sysex_command = this.stored_input_data[0];
|
|
233
|
+ sysex_data = this.stored_input_data.slice(1, this.sysex_bytes_read);
|
|
234
|
+ return this.emit('sysex', {
|
|
235
|
+ command: sysex_command,
|
|
236
|
+ data: sysex_data
|
|
237
|
+ });
|
|
238
|
+ } else {
|
|
239
|
+ this.stored_input_data[this.sysex_bytes_read] = input_data;
|
|
240
|
+ return this.sysex_bytes_read += 1;
|
|
241
|
+ }
|
|
242
|
+ } else if (this.wait_for_data > 0 && input_data < 128) {
|
|
243
|
+ this.wait_for_data -= 1;
|
|
244
|
+ this.stored_input_data[this.wait_for_data] = input_data;
|
|
245
|
+ if (this.execute_multi_byte_command !== 0 && this.wait_for_data === 0) {
|
|
246
|
+ switch (this.execute_multi_byte_command) {
|
|
247
|
+ case ArduinoFirmata.DIGITAL_MESSAGE:
|
|
248
|
+ input_data = (this.stored_input_data[0] << 7) + this.stored_input_data[1];
|
|
249
|
+ diff = this.digital_input_data[this.multi_byte_channel] ^ input_data;
|
|
250
|
+ this.digital_input_data[this.multi_byte_channel] = input_data;
|
|
251
|
+ if (this.listeners('digitalChange').length > 0) {
|
|
252
|
+ results = [];
|
|
253
|
+ for (i = j = 0; j <= 13; i = ++j) {
|
|
254
|
+ if (((0x01 << i) & diff) > 0) {
|
|
255
|
+ stat = (input_data & diff) > 0;
|
|
256
|
+ results.push(this.emit('digitalChange', {
|
|
257
|
+ pin: i + this.multi_byte_channel * 8,
|
|
258
|
+ value: stat,
|
|
259
|
+ old_value: !stat
|
|
260
|
+ }));
|
|
261
|
+ } else {
|
|
262
|
+ results.push(void 0);
|
|
263
|
+ }
|
|
264
|
+ }
|
|
265
|
+ return results;
|
|
266
|
+ }
|
|
267
|
+ break;
|
|
268
|
+ case ArduinoFirmata.ANALOG_MESSAGE:
|
|
269
|
+ analog_value = (this.stored_input_data[0] << 7) + this.stored_input_data[1];
|
|
270
|
+ old_analog_value = this.analogRead(this.multi_byte_channel);
|
|
271
|
+ this.analog_input_data[this.multi_byte_channel] = analog_value;
|
|
272
|
+ if (old_analog_value !== analog_value) {
|
|
273
|
+ return this.emit('analogChange', {
|
|
274
|
+ pin: this.multi_byte_channel,
|
|
275
|
+ value: analog_value,
|
|
276
|
+ old_value: old_analog_value
|
|
277
|
+ });
|
|
278
|
+ }
|
|
279
|
+ break;
|
|
280
|
+ case ArduinoFirmata.REPORT_VERSION:
|
|
281
|
+ this.boardVersion = this.stored_input_data[1] + "." + this.stored_input_data[0];
|
|
282
|
+ return this.emit('boardVersion', this.boardVersion);
|
|
283
|
+ }
|
|
284
|
+ }
|
|
285
|
+ } else {
|
|
286
|
+ if (input_data < 0xF0) {
|
|
287
|
+ command = input_data & 0xF0;
|
|
288
|
+ this.multi_byte_channel = input_data & 0x0F;
|
|
289
|
+ } else {
|
|
290
|
+ command = input_data;
|
|
291
|
+ }
|
|
292
|
+ if (command === ArduinoFirmata.START_SYSEX) {
|
|
293
|
+ this.parsing_sysex = true;
|
|
294
|
+ return this.sysex_bytes_read = 0;
|
|
295
|
+ } else if (command === ArduinoFirmata.DIGITAL_MESSAGE || command === ArduinoFirmata.ANALOG_MESSAGE || command === ArduinoFirmata.REPORT_VERSION) {
|
|
296
|
+ this.wait_for_data = 2;
|
|
297
|
+ return this.execute_multi_byte_command = command;
|
|
298
|
+ }
|
|
299
|
+ }
|
|
300
|
+ };
|
|
301
|
+
|
|
302
|
+ return ArduinoFirmata;
|
|
303
|
+
|
|
304
|
+ })(events.EventEmitter2);
|
|
305
|
+
|
|
306
|
+}).call(this);
|