firmata

firmata

lib/firmata.js
  • author: Julian Gautier

Module Dependencies

var SerialPort = require('serialport').SerialPort,
    sys = require('sys'),
    events = require('events');

constants

var PIN_MODE = xF4,
    REPORT_DIGITAL = xD0,
    REPORT_ANALOG = xC0,
    DIGITAL_MESSAGE = x90,
    START_SYSEX = xF0,
    END_SYSEX = xF7,
    QUERY_FIRMWARE = x79,
    REPORT_VERSION = xF9,
    ANALOG_MESSAGE = xE0,
    CAPABILITY_QUERY = x6B,
    CAPABILITY_RESPONSE = x6C,
    PIN_STATE_QUERY = x6D,
    PIN_STATE_RESPONSE = x6E,
    ANALOG_MAPPING_QUERY = x69,
    ANALOG_MAPPING_RESPONSE = x6A,
    I2C_REQUEST = x76,
    I2C_REPLY = x77,
    I2C_CONFIG = x78,
    STRING_DATA = x71
  • class: The Board object represents an arduino board.
  • augments: EventEmitter

  • param: String port This is the serial port the arduino is connected to.

  • param: function function A function to be called when the arduino is ready to communicate.

  • property: MODES All the modes available for pins on this arduino board.

  • property: I2CMODES_ All the I2C modes available.
  • property: HIGH A constant to set a pins value to HIGH when the pin is set to an output.
  • property: LOW A constant to set a pins value to LOW when the pin is set to an output.
  • property: pins An array of pin object literals.
  • property: analogPins An array of analog pins and their corresponding indexes in the pins array.
  • property: version An object indicating the major and minor version of the firmware currently running.
  • property: firmware An object indication the name, major and minor version of the firmware currently running.
  • property: currentBuffer An array holding the current bytes received from the arduino.

  • property: SerialPort sp The serial port object used to communicate with the arduino.

var Board = function(port, callback) {
        events.EventEmitter.call(this);
        var board = this;
        this.MODES = {
            INPUT: x00,
            OUTPUT: x01,
            ANALOG: x02,
            PWM: x03,
            SERVO: x04
        };
        this.I2C_MODES = {
            WRITE: x00,
            READ: 1,
            CONTINUOUS_READ: 2,
            STOP_READING: 3
        };
        this.HIGH = 1;
        this.LOW = 0;
        this.pins = [];
        this.analogPins = [];
        this.version = {};
        this.firmware = {};
        this.currentBuffer = [];
        this.sp = new SerialPort(port, {
            baudrate: 57600,
            buffersize: 1
        });
        this.sp.on('data', function(data) {
            //we dont want to push 0 as the first byte on our buffer
            if ((board.currentBuffer.length === 0 && data[0] !== 0 || board.currentBuffer.length)) {
                board.currentBuffer.push(data[0]);
            }
            //a MIDI or SYSEX command function we are going to call
            var cmdFunc;
            var cmd;
            //if the first byte is START_SYSEX and last byte is END_SYSEX we have a SYSEX command.
            if (board.currentBuffer[0] == START_SYSEX && board.currentBuffer[board.currentBuffer.length - 1] == END_SYSEX) {
                cmdFunc = SYSEX_RESPONSE[board.currentBuffer[1]];
                //if the first byte is not a START_SYSEX and we have 3 bytes we might have a MIDI Command
            } else if (board.currentBuffer.length == 3 && board.currentBuffer[0] != START_SYSEX) {
                //commands under 0xF0 we have a multi byte command
                if (board.currentBuffer[0] < 240) {
                    cmd = board.currentBuffer[0] & xF0;
                } else {
                    cmd = board.currentBuffer[0];
                }
                cmdFunc = MIDI_RESPONSE[cmd];
            }
            //if a function is found we will call it
            if (cmdFunc) {
                //call function with board object
                cmdFunc(board);
                //reset currentBuffer so we can receive the next command
                board.currentBuffer = [];
            }
        });
        this.sp.on('error', function(string) {
            callback(string);
        });
        this.reportVersion(function() {
            board.queryCapabilities(function() {
                board.queryAnalogMapping(function() {
                    var pinsToQuery = [];
                    for (i = 0; i < board.pins.length; i++) {
                        pinsToQuery[i] = i;
                    }
                    for (i = 0; i < pinsToQuery.length; i++) {
                        if (i == pinsToQuery.length - 1) {
                            board.queryPinState(pinsToQuery[i], function() {
                                callback();
                            });
                        } else {
                            board.queryPinState(pinsToQuery[i], function() {});
                        }
                    }
                });
            });
        });
    };
sys.inherits(Board, events.EventEmitter);

Asks the arduino to tell us its version. ##

  • param: function callback A function to be called when the arduino has reported its version.

Board.prototype.reportVersion = function(callback) {
    this.once('reportversion', callback);
    this.sp.write(REPORT_VERSION);
};

Asks the arduino to tell us its firmware version. ##

  • param: function callback A function to be called when the arduino has reported its firmware version.

Board.prototype.queryFirmware = function(callback) {
    this.once('queryfirmware', callback);
    this.sp.write([START_SYSEX, QUERY_FIRMWARE, END_SYSEX]);
};

Asks the arduino to read analog data. ##

  • param: number pin The pin to read analog data

  • param: function callback A function to call when we have the analag data.

Board.prototype.analogRead = function(pin, callback) {
    this.addListener('analog-read-' + pin, callback);
};

Asks the arduino to write an analog message. ##

  • param: number pin The pin to write analog data to.

  • param: nubmer value The data to write to the pin between 0 and 255.

Board.prototype.analogWrite = function(pin, value) {
    this.pins[pin].value = value;
    this.sp.write([ANALOG_MESSAGE | pin, value & x7F, (value >> 7) & x7F]);
};

Asks the arduino to move a servo ##

  • param: number pin The pin the servo is connected to

  • param: number value The degrees to move the servo to.

Board.prototype.servoWrite = function(pin, value) {
    this.analogWrite.apply(this, arguments);
};

Asks the arduino to set the pin to a certain mode. ##

  • param: number pin The pin you want to change the mode of.

  • param: number mode The mode you want to set. Must be one of board.MODES

Board.prototype.pinMode = function(pin, mode) {
    this.pins[pin].mode = mode;
    this.sp.write([PIN_MODE, pin, mode]);
};

Asks the arduino to write a value to a digital pin ##

  • param: number pin The pin you want to write a value to.

  • param: value value The value you want to write. Must be board.HIGH or board.LOW

Board.prototype.digitalWrite = function(pin, value) {
    var port = Math.floor(pin / 8);
    var portValue = 0;
    this.pins[pin].value = value;
    for (var i = 0; i < 8; i++) {
        if (this.pins[8 * port + i].value) portValue |= (1 << i);
    }
    this.sp.write([DIGITAL_MESSAGE | port, portValue & x7F, (portValue >> 7) & x7F]);
};

Asks the arduino to read digital data ##

  • param: number pin The pin to read data from

  • param: function callback The function to call when data has been received

Board.prototype.digitalRead = function(pin, callback) {
    this.addListener('digital-read-' + pin, callback);
};

Asks the arduino to tell us its capabilities ##

  • param: function callback A function to call when we receive the capabilities

Board.prototype.queryCapabilities = function(callback) {
    this.once('capability-query', callback);
    this.sp.write([START_SYSEX, CAPABILITY_QUERY, END_SYSEX]);
};

Asks the arduino to tell us its analog pin mapping ##

  • param: function callback A function to call when we receive the pin mappings.

Board.prototype.queryAnalogMapping = function(callback) {
    this.once('analog-mapping-query', callback);
    this.sp.write([START_SYSEX, ANALOG_MAPPING_QUERY, END_SYSEX]);
};

Asks the arduino to tell us the current state of a pin ##

  • param: number pin The pin we want to the know the state of

  • param: function callback A function to call when we receive the pin state.

Board.prototype.queryPinState = function(pin, callback) {
    this.once('pin-state-' + pin, callback);
    this.sp.write([START_SYSEX, PIN_STATE_QUERY, pin, END_SYSEX]);
};

Sends a I2C config request to the arduino board with an optional value in microseconds to delay an I2C Read. Must be called before an I2C Read or Write ##

  • param: number delay in microseconds to set for I2C Read

Board.prototype.sendI2CConfig=function(delay){
  var delay = delay || 0;
  this.sp.write([START_SYSEX,I2C_CONFIG,(delay & xFF),((delay >> 8) & xFF),END_SYSEX]);
};

Asks the arduino to send an I2C request to a device ##

  • param: number slaveAddress The address of the I2C device

  • param: Array bytes The bytes to send to the device

Board.prototype.sendI2CWriteRequest = function(slaveAddress, bytes) {
    var bytes = bytes || [];
    var data = [];
    data.push(START_SYSEX);
    data.push(I2C_REQUEST);
    data.push(slaveAddress);
    data.push(this.I2C_MODES.WRITE << 3);
    for (var i = 0, length = bytes.length; i < length; i++) {
        data.push(bytes[i] & x7F);
        data.push((bytes[i] >> 7) & x7F);
    }
    data.push(END_SYSEX);
    this.sp.write(data);
};

Asks the arduino to request bytes from an I2C device ##

  • param: number slaveAddress The address of the I2C device

  • param: number numBytes The number of bytes to receive.

  • param: function callback A function to call when we have received the bytes.

Board.prototype.sendI2CReadRequest = function(slaveAddress, numBytes, callback) {
    this.sp.write([START_SYSEX, I2C_REQUEST, slaveAddress, this.I2C_MODES.READ << 3, numBytes & x7F, (numBytes >> 7) & x7F, END_SYSEX]);
    this.once('I2C-reply-' + slaveAddress, callback);
};
module.exports = {
    Board: Board
};