arduino-firmata.js (10835B)
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);